From a6ba427fe1c854baa6c61b2118ecbe75fe16c8b0 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Tue, 14 May 2024 11:55:35 +0200 Subject: [PATCH 01/18] feat: initial nearest neighbor ui --- app/src/app/analysis/analysis-page.tsx | 13 +-- .../app/analysis/analysis-selection-menu.tsx | 33 ++++++++ .../nearest-neighbor-menu-item.tsx | 32 +++++++ .../nearest-neighbor-modal.tsx | 84 +++++++++++++++++++ ...ce-button.tsx => resistance-menu-item.tsx} | 14 ++-- app/src/app/icons/bacteria.tsx | 34 ++++---- .../common/config/column_config.py | 5 +- 7 files changed, 181 insertions(+), 34 deletions(-) create mode 100644 app/src/app/analysis/analysis-selection-menu.tsx create mode 100644 app/src/app/analysis/nearest-neighbor/nearest-neighbor-menu-item.tsx create mode 100644 app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx rename app/src/app/analysis/resistance/{resistance-button.tsx => resistance-menu-item.tsx} (87%) diff --git a/app/src/app/analysis/analysis-page.tsx b/app/src/app/analysis/analysis-page.tsx index ec518c7e..86d9c13e 100644 --- a/app/src/app/analysis/analysis-page.tsx +++ b/app/src/app/analysis/analysis-page.tsx @@ -46,9 +46,9 @@ import ExportButton from "./export/export-button"; import { ColumnConfigNode } from "./data-table/column-config-node"; import { AnalysisResultAllOfQcFailedTests } from "sap-client/models/AnalysisResultAllOfQcFailedTests"; import { Judgement } from "./judgement/judgement"; -import { ResistanceButton } from "./resistance/resistance-button"; import { Health } from "./health/health"; import { Debug } from "./debug"; +import { AnalysisSelectionMenu } from "./analysis-selection-menu"; // When the fields in this array are 'approved', a given sequence is rendered // as 'approved' also. @@ -58,7 +58,7 @@ export default function AnalysisPage() { const { t } = useTranslation(); const dispatch = useDispatch(); const toast = useToast(); - + const [detailsIsolate, setDetailsIsolate] = useState< React.ComponentProps["isolate"] >(); @@ -129,7 +129,7 @@ export default function AnalysisPage() { isClosable: true, }); } - }, [updateStatus, toast]) + }, [updateStatus, toast]); const [lastUpdatedRow, setLastUpdatedRow] = React.useState(null); const [lastUpdatedColumns, setLastUpdatedColumns] = React.useState([]); @@ -596,6 +596,11 @@ export default function AnalysisPage() { onNarrowHandler={onNarrowHandler} getDependentColumns={getDependentColumns} /> + + {!pageState.isNarrowed ? ( {(columnOrder || columns.map((x) => x.accessor as string)).map( @@ -611,8 +616,6 @@ export default function AnalysisPage() { )} ) : null} - - x.accessor) as any} diff --git a/app/src/app/analysis/analysis-selection-menu.tsx b/app/src/app/analysis/analysis-selection-menu.tsx new file mode 100644 index 00000000..0cb843e8 --- /dev/null +++ b/app/src/app/analysis/analysis-selection-menu.tsx @@ -0,0 +1,33 @@ +import { AnalysisResult } from "sap-client"; +import { DataTableSelection } from "./data-table/data-table"; +import { HamburgerIcon } from "@chakra-ui/icons"; +import { ResistanceMenuItem } from "./resistance/resistance-menu-item"; +import { NearestNeighborMenuItem } from "./nearest-neighbor/nearest-neighbor-menu-item"; +import { Menu, MenuList, MenuButton, Button } from "@chakra-ui/react"; + +type Props = { + selection: DataTableSelection; + isNarrowed: boolean; +}; + +export const AnalysisSelectionMenu = (props: Props) => { + const { selection, isNarrowed } = props; + + return ( +
+ + } + disabled={isNarrowed || Object.keys(selection).length == 0} + > + Selection + + + + + + +
+ ); +}; diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-menu-item.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-menu-item.tsx new file mode 100644 index 00000000..dbee3497 --- /dev/null +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-menu-item.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { MenuItem } from "@chakra-ui/react"; +import { AnalysisResult } from "sap-client"; +import { DataTableSelection } from "../data-table/data-table"; +import { useDisclosure } from "@chakra-ui/react"; +import { PlusSquareIcon } from "@chakra-ui/icons"; +import { NearestNeighborModal } from "./nearest-neighbor-modal"; + +type Props = { + selection: DataTableSelection; +}; + +export const NearestNeighborMenuItem = (props: Props) => { + const { selection } = props; + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + <> + {isOpen ? ( + + ) : null} + } + onClick={onOpen} + > + Nearest Neighbor + + + ); +}; diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx new file mode 100644 index 00000000..fd226e8c --- /dev/null +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -0,0 +1,84 @@ +import React, { useCallback, useState } from "react"; +import { + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, + Spinner, +} from "@chakra-ui/react"; +import { AnalysisResult } from "sap-client"; +import { DataTableSelection } from "../data-table/data-table"; +import { + Button, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, +} from "@chakra-ui/react"; +import { useTranslation } from "react-i18next"; + +type Props = { + selection: DataTableSelection; + onClose: () => void; +}; + +export const NearestNeighborModal = (props: Props) => { + const { t } = useTranslation(); + const { selection, onClose } = props; + const [isSearching, setIsSearching] = useState(false); + const [cutoff, setCutoff] = React.useState(15); + const onChangeCutoff = (value) => setCutoff(value); + + const onSearch = useCallback(() => { + setIsSearching(true); + alert( + `Search, cutoff ${cutoff}, ids ` + + Object.values(selection) + .map((s) => s.original.id) + .join(", ") + ); + }, [setIsSearching, selection, cutoff]); + + return ( + + + + {`${t("Nearest Neighbor")}`} + + + {!isSearching ? ( + <> + Cutoff: + + + + + + + + + ) : ( + + )} + + + + + + + + ); +}; diff --git a/app/src/app/analysis/resistance/resistance-button.tsx b/app/src/app/analysis/resistance/resistance-menu-item.tsx similarity index 87% rename from app/src/app/analysis/resistance/resistance-button.tsx rename to app/src/app/analysis/resistance/resistance-menu-item.tsx index 756bd08e..8aa9e94a 100644 --- a/app/src/app/analysis/resistance/resistance-button.tsx +++ b/app/src/app/analysis/resistance/resistance-menu-item.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from "react"; -import { IconButton, useToast } from "@chakra-ui/react"; +import { MenuItem, useToast } from "@chakra-ui/react"; import { AnalysisResult } from "sap-client"; import { DataTableSelection } from "../data-table/data-table"; import { Bacteria } from "app/icons/bacteria"; @@ -21,7 +21,7 @@ type Props = { selection: DataTableSelection; }; -export const ResistanceButton = (props: Props) => { +export const ResistanceMenuItem = (props: Props) => { const { t } = useTranslation(); const toast = useToast(); const { selection } = props; @@ -60,18 +60,16 @@ export const ResistanceButton = (props: Props) => { - - } - size="sm" - ml="1" onClick={onClickCallback} - /> + > + Resistance + ); }; diff --git a/app/src/app/icons/bacteria.tsx b/app/src/app/icons/bacteria.tsx index eb591fa1..a60d5e4b 100644 --- a/app/src/app/icons/bacteria.tsx +++ b/app/src/app/icons/bacteria.tsx @@ -1,20 +1,14 @@ -export const Bacteria = () => { - return ( - - - - ); -}; +import { createIcon } from "@chakra-ui/react"; + +export const Bacteria = createIcon({ + displayName: "Bacteria", + viewBox: "0 0 500 500", + path: ( + + ), +}); diff --git a/bifrost/bifrost_queue_broker/common/config/column_config.py b/bifrost/bifrost_queue_broker/common/config/column_config.py index 379825cf..b10b15b5 100644 --- a/bifrost/bifrost_queue_broker/common/config/column_config.py +++ b/bifrost/bifrost_queue_broker/common/config/column_config.py @@ -38,7 +38,10 @@ def columns() -> List[Dict[str, str]]: analysis = AnalysisResult() cols: Dict[str, ColumnDict] = {} # build up dictionary of default configs - for attr, _ in analysis.openapi_types.items(): + openapi_columns = analysis.openapi_types.items() + for attr, _ in openapi_columns: + if attr == "id": + continue cols.update({attr: gen_default_column(attr)}) # apply configuration file on top with open(PATH + "/column-config.jsonc") as js_file: From 2756a981119da9e1dacddbe1257620b32f4d504d Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Wed, 15 May 2024 12:46:36 +0200 Subject: [PATCH 02/18] feat: initial bio api and nearest neighbors ui --- Makefile | 18 +- .../nearest-neighbor-modal.tsx | 24 +- .../nearest-neighbor-query-configs.ts | 22 + .../sap-client/apis/NearestNeighborsApi.ts | 79 + app/src/sap-client/apis/index.ts | 1 + .../sap-client/models/BioApiJobResponse.ts | 66 + app/src/sap-client/models/BioApiStatus.ts | 32 + .../models/NearestNeighborsRequest.ts | 52 + app/src/sap-client/models/index.ts | 3 + docker-compose.yml | 12 + openapi_specs/SOFI/SOFI.yaml | 55 + openapi_specs/bio_api.yaml | 575 +++++ web/openapi_specs/SOFI/SOFI.yaml | 55 + web/openapi_specs/bio_api.yaml | 575 +++++ .../nearest_neighbors_controller.py | 20 + web/src/SAP/generated/models/__init__.py | 3 + .../generated/models/bio_api_job_response.py | 120 + .../SAP/generated/models/bio_api_status.py | 45 + .../models/nearest_neighbors_request.py | 96 + web/src/SAP/generated/openapi/openapi.yaml | 58 + .../test/test_nearest_neighbors_controller.py | 41 + .../controllers/NearestNeighborsController.py | 17 + web/src/services/__init__.py | 2 +- web/src/services/bio_api/__init__.py | 0 web/src/services/bio_api/openapi/__init__.py | 27 + .../services/bio_api/openapi/api/__init__.py | 3 + .../bio_api/openapi/api/distances_api.py | 282 +++ .../openapi/api/nearest_neighbors_api.py | 281 +++ .../services/bio_api/openapi/api/trees_api.py | 280 +++ .../services/bio_api/openapi/api_client.py | 849 +++++++ .../services/bio_api/openapi/apis/__init__.py | 19 + .../services/bio_api/openapi/configuration.py | 443 ++++ .../services/bio_api/openapi/exceptions.py | 159 ++ .../bio_api/openapi/model/__init__.py | 5 + .../openapi/model/common_post_response.py | 279 +++ .../model/distance_matrix_get_response.py | 317 +++ .../openapi/model/distance_matrix_request.py | 279 +++ .../openapi/model/distance_matrix_result.py | 265 +++ .../model/hc_tree_calc_get_response.py | 312 +++ .../openapi/model/hc_tree_calc_request.py | 276 +++ .../openapi/model/http_validation_error.py | 261 +++ .../services/bio_api/openapi/model/message.py | 261 +++ .../model/nearest_neighbors_get_response.py | 325 +++ .../model/nearest_neighbors_request.py | 289 +++ .../bio_api/openapi/model/neighbor.py | 267 +++ .../services/bio_api/openapi/model/status.py | 289 +++ .../bio_api/openapi/model/validation_error.py | 273 +++ .../services/bio_api/openapi/model_utils.py | 1992 +++++++++++++++++ .../bio_api/openapi/models/__init__.py | 24 + web/src/services/bio_api/openapi/rest.py | 292 +++ web/src/services/bio_api/openapi_README.md | 126 ++ 51 files changed, 10442 insertions(+), 4 deletions(-) create mode 100644 app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts create mode 100644 app/src/sap-client/apis/NearestNeighborsApi.ts create mode 100644 app/src/sap-client/models/BioApiJobResponse.ts create mode 100644 app/src/sap-client/models/BioApiStatus.ts create mode 100644 app/src/sap-client/models/NearestNeighborsRequest.ts create mode 100644 openapi_specs/bio_api.yaml create mode 100644 web/openapi_specs/bio_api.yaml create mode 100644 web/src/SAP/generated/controllers/nearest_neighbors_controller.py create mode 100644 web/src/SAP/generated/models/bio_api_job_response.py create mode 100644 web/src/SAP/generated/models/bio_api_status.py create mode 100644 web/src/SAP/generated/models/nearest_neighbors_request.py create mode 100644 web/src/SAP/generated/test/test_nearest_neighbors_controller.py create mode 100644 web/src/SAP/src/controllers/NearestNeighborsController.py create mode 100644 web/src/services/bio_api/__init__.py create mode 100644 web/src/services/bio_api/openapi/__init__.py create mode 100644 web/src/services/bio_api/openapi/api/__init__.py create mode 100644 web/src/services/bio_api/openapi/api/distances_api.py create mode 100644 web/src/services/bio_api/openapi/api/nearest_neighbors_api.py create mode 100644 web/src/services/bio_api/openapi/api/trees_api.py create mode 100644 web/src/services/bio_api/openapi/api_client.py create mode 100644 web/src/services/bio_api/openapi/apis/__init__.py create mode 100644 web/src/services/bio_api/openapi/configuration.py create mode 100644 web/src/services/bio_api/openapi/exceptions.py create mode 100644 web/src/services/bio_api/openapi/model/__init__.py create mode 100644 web/src/services/bio_api/openapi/model/common_post_response.py create mode 100644 web/src/services/bio_api/openapi/model/distance_matrix_get_response.py create mode 100644 web/src/services/bio_api/openapi/model/distance_matrix_request.py create mode 100644 web/src/services/bio_api/openapi/model/distance_matrix_result.py create mode 100644 web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py create mode 100644 web/src/services/bio_api/openapi/model/hc_tree_calc_request.py create mode 100644 web/src/services/bio_api/openapi/model/http_validation_error.py create mode 100644 web/src/services/bio_api/openapi/model/message.py create mode 100644 web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py create mode 100644 web/src/services/bio_api/openapi/model/nearest_neighbors_request.py create mode 100644 web/src/services/bio_api/openapi/model/neighbor.py create mode 100644 web/src/services/bio_api/openapi/model/status.py create mode 100644 web/src/services/bio_api/openapi/model/validation_error.py create mode 100644 web/src/services/bio_api/openapi/model_utils.py create mode 100644 web/src/services/bio_api/openapi/models/__init__.py create mode 100644 web/src/services/bio_api/openapi/rest.py create mode 100644 web/src/services/bio_api/openapi_README.md diff --git a/Makefile b/Makefile index 63c971bf..2e3f936d 100644 --- a/Makefile +++ b/Makefile @@ -112,6 +112,21 @@ ${mkfile_dir}/web/src/services/lims/openapi : ${mkfile_dir}/openapi_specs/lims.v --global-property apiTests=false,apiDocs=false \ --global-property modelTests=false,modelDocs=false +${mkfile_dir}/web/src/services/bio_api/openapi : ${mkfile_dir}/openapi_specs/bio_api.yaml + # Generate bio_api client for flask api + rm -rf ${mkfile_dir}/web/src/services/bio_api/openapi + docker run --rm -v "${mkfile_dir}:/local" \ + --user ${mkfile_user} \ + "openapitools/openapi-generator:cli-v5.2.0" \ + generate \ + -i /local/openapi_specs/bio_api.yaml \ + -g python \ + -o /local \ + --additional-properties packageName=web.src.services.bio_api.openapi \ + --additional-properties generateSourceCodeOnly=true \ + --global-property apiTests=false,apiDocs=false \ + --global-property modelTests=false,modelDocs=false + ${mkfile_dir}/bifrost/bifrost_queue_broker/api_clients/lims_client : ${mkfile_dir}/openapi_specs/lims.v1.yaml # Generate LIMS client for request broker rm -rf ${mkfile_dir}/bifrost/bifrost_queue_broker/api_clients/lims_client @@ -161,7 +176,7 @@ ${mkfile_dir}/bifrost/bifrost_queue_broker/api_clients/lims_client : ${mkfile_di ${mkfile_dir}/app/node_modules/ : ${mkfile_dir}/app/package.json pushd ${mkfile_dir}/app && npx yarn install -build: ${mkfile_dir}/app/src/sap-client ${mkfile_dir}/web/src/SAP/generated ${mkfile_dir}/web/src/services/lims/openapi +build: ${mkfile_dir}/app/src/sap-client ${mkfile_dir}/web/src/SAP/generated ${mkfile_dir}/web/src/services/lims/openapi ${mkfile_dir}/web/src/services/bio_api/openapi CURRENT_UID=${mkfile_user} docker-compose build --no-cache install: @@ -177,6 +192,7 @@ RUN_DEPS += ${mkfile_dir}/web/src/services/lims/openapi ${mkfile_dir}/app/node_m RUN_DEPS += ${mkfile_dir}/bifrost/bifrost_queue_broker/api_clients/tbr_client RUN_DEPS += ${mkfile_dir}/bifrost/bifrost_queue_broker/api_clients/lims_client RUN_DEPS += ${mkfile_dir}/.env +RUN_DEPS += ${mkfile_dir}/web/src/services/bio_api/openapi run: $(RUN_DEPS) CURRENT_UID=${mkfile_user} docker-compose -f ${mkfile_dir}/docker-compose.yml -f ${mkfile_dir}/docker-compose.local.yml up --build $(ARGS) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index fd226e8c..3d782ff3 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { NumberDecrementStepper, NumberIncrementStepper, @@ -20,6 +20,9 @@ import { ModalCloseButton, } from "@chakra-ui/react"; import { useTranslation } from "react-i18next"; +import { useDispatch } from "react-redux"; +import { requestAsync } from "redux-query"; +import { nearestNeighborsRequest } from "./nearest-neighbor-query-configs"; type Props = { selection: DataTableSelection; @@ -33,6 +36,17 @@ export const NearestNeighborModal = (props: Props) => { const [cutoff, setCutoff] = React.useState(15); const onChangeCutoff = (value) => setCutoff(value); + const dispatch = useDispatch(); + + useEffect(() => { + // TODO + dispatch( + requestAsync({ + ...nearestNeighborsRequest({ id: "6211e8bc207a1f796ec0b69f", cutoff: 15 }), + }) + ); + }, [dispatch]); + const onSearch = useCallback(() => { setIsSearching(true); alert( @@ -53,7 +67,12 @@ export const NearestNeighborModal = (props: Props) => { {!isSearching ? ( <> Cutoff: - + @@ -61,6 +80,7 @@ export const NearestNeighborModal = (props: Props) => { + // TODO: unknowns_are_diffs ) : ( )} diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts new file mode 100644 index 00000000..5cf4c06f --- /dev/null +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts @@ -0,0 +1,22 @@ +import { BioApiJobResponse, NearestNeighborsRequest, post } from "sap-client"; +import { getUrl } from "service"; + +// TODO +export const nearestNeighborsRequest = (request: NearestNeighborsRequest) => { + const base = post({ body: request }); + + base.url = getUrl(base.url); + + base.transform = (response: BioApiJobResponse) => { + return {}; + }; + + // base.update = { + // health: (oldValue, newValue) => { + // return merge({}, cloneDeep(oldValue), newValue); + // }, + // }; + + base.force = true; + return base; +}; diff --git a/app/src/sap-client/apis/NearestNeighborsApi.ts b/app/src/sap-client/apis/NearestNeighborsApi.ts new file mode 100644 index 00000000..45d1b6f4 --- /dev/null +++ b/app/src/sap-client/apis/NearestNeighborsApi.ts @@ -0,0 +1,79 @@ +// tslint:disable +/** + * SOFI + * SOFI Sekvensanalyseplatform + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import { HttpMethods, QueryConfig, ResponseBody, ResponseText } from 'redux-query'; +import * as runtime from '../runtime'; +import { + BioApiJobResponse, + BioApiJobResponseFromJSON, + BioApiJobResponseToJSON, + NearestNeighborsRequest, + NearestNeighborsRequestFromJSON, + NearestNeighborsRequestToJSON, +} from '../models'; + +export interface PostRequest { + body: NearestNeighborsRequest; +} + + +/** + * Nearest Neighbors + */ +function postRaw(requestParameters: PostRequest, requestConfig: runtime.TypedQueryConfig = {}): QueryConfig { + if (requestParameters.body === null || requestParameters.body === undefined) { + throw new runtime.RequiredError('body','Required parameter requestParameters.body was null or undefined when calling post.'); + } + + let queryParameters = null; + + + const headerParameters : runtime.HttpHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + + const { meta = {} } = requestConfig; + + meta.authType = ['bearer']; + const config: QueryConfig = { + url: `${runtime.Configuration.basePath}/nearest_neighbors`, + meta, + update: requestConfig.update, + queryKey: requestConfig.queryKey, + optimisticUpdate: requestConfig.optimisticUpdate, + force: requestConfig.force, + rollback: requestConfig.rollback, + options: { + method: 'POST', + headers: headerParameters, + }, + body: queryParameters || NearestNeighborsRequestToJSON(requestParameters.body), + }; + + const { transform: requestTransform } = requestConfig; + if (requestTransform) { + config.transform = (body: ResponseBody, text: ResponseBody) => requestTransform(BioApiJobResponseFromJSON(body), text); + } + + return config; +} + +/** +* Nearest Neighbors +*/ +export function post(requestParameters: PostRequest, requestConfig?: runtime.TypedQueryConfig): QueryConfig { + return postRaw(requestParameters, requestConfig); +} + diff --git a/app/src/sap-client/apis/index.ts b/app/src/sap-client/apis/index.ts index 88aafdfe..5050907e 100644 --- a/app/src/sap-client/apis/index.ts +++ b/app/src/sap-client/apis/index.ts @@ -3,6 +3,7 @@ export * from './ApprovalApi'; export * from './ComparativeAnalysisApi'; export * from './GdprApi'; export * from './HealthApi'; +export * from './NearestNeighborsApi'; export * from './SamplesApi'; export * from './UploadApi'; export * from './UserApi'; diff --git a/app/src/sap-client/models/BioApiJobResponse.ts b/app/src/sap-client/models/BioApiJobResponse.ts new file mode 100644 index 00000000..261dd54e --- /dev/null +++ b/app/src/sap-client/models/BioApiJobResponse.ts @@ -0,0 +1,66 @@ +// tslint:disable +/** + * SOFI + * SOFI Sekvensanalyseplatform + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + BioApiStatus, + BioApiStatusFromJSON, + BioApiStatusToJSON, +} from './'; + +/** + * + * @export + * @interface BioApiJobResponse + */ +export interface BioApiJobResponse { + /** + * + * @type {string} + * @memberof BioApiJobResponse + */ + job_id?: string; + /** + * + * @type {string} + * @memberof BioApiJobResponse + */ + created_at?: string; + /** + * + * @type {BioApiStatus} + * @memberof BioApiJobResponse + */ + status?: BioApiStatus; +} + +export function BioApiJobResponseFromJSON(json: any): BioApiJobResponse { + return { + 'job_id': !exists(json, 'job_id') ? undefined : json['job_id'], + 'created_at': !exists(json, 'created_at') ? undefined : json['created_at'], + 'status': !exists(json, 'status') ? undefined : BioApiStatusFromJSON(json['status']), + }; +} + +export function BioApiJobResponseToJSON(value?: BioApiJobResponse): any { + if (value === undefined) { + return undefined; + } + return { + 'job_id': value.job_id, + 'created_at': value.created_at, + 'status': BioApiStatusToJSON(value.status), + }; +} + + diff --git a/app/src/sap-client/models/BioApiStatus.ts b/app/src/sap-client/models/BioApiStatus.ts new file mode 100644 index 00000000..c417468c --- /dev/null +++ b/app/src/sap-client/models/BioApiStatus.ts @@ -0,0 +1,32 @@ +// tslint:disable +/** + * SOFI + * SOFI Sekvensanalyseplatform + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @enum {string} + */ +export enum BioApiStatus { + init = 'init', + completed = 'completed', + error = 'error' +} + +export function BioApiStatusFromJSON(json: any): BioApiStatus { + return json as BioApiStatus; +} + +export function BioApiStatusToJSON(value?: BioApiStatus): any { + return value as any; +} + diff --git a/app/src/sap-client/models/NearestNeighborsRequest.ts b/app/src/sap-client/models/NearestNeighborsRequest.ts new file mode 100644 index 00000000..ce2d41b4 --- /dev/null +++ b/app/src/sap-client/models/NearestNeighborsRequest.ts @@ -0,0 +1,52 @@ +// tslint:disable +/** + * SOFI + * SOFI Sekvensanalyseplatform + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * Parameters for a REST request for a nearest neighbors calculation. + * @export + * @interface NearestNeighborsRequest + */ +export interface NearestNeighborsRequest { + /** + * + * @type {string} + * @memberof NearestNeighborsRequest + */ + id: string; + /** + * + * @type {number} + * @memberof NearestNeighborsRequest + */ + cutoff: number; +} + +export function NearestNeighborsRequestFromJSON(json: any): NearestNeighborsRequest { + return { + 'id': json['id'], + 'cutoff': json['cutoff'], + }; +} + +export function NearestNeighborsRequestToJSON(value?: NearestNeighborsRequest): any { + if (value === undefined) { + return undefined; + } + return { + 'id': value.id, + 'cutoff': value.cutoff, + }; +} + + diff --git a/app/src/sap-client/models/index.ts b/app/src/sap-client/models/index.ts index 14716fd0..1714decb 100644 --- a/app/src/sap-client/models/index.ts +++ b/app/src/sap-client/models/index.ts @@ -7,6 +7,8 @@ export * from './ApprovalAllOf'; export * from './ApprovalRequest'; export * from './ApprovalStatus'; export * from './BaseMetadata'; +export * from './BioApiJobResponse'; +export * from './BioApiStatus'; export * from './Column'; export * from './DataClearance'; export * from './Exception'; @@ -17,6 +19,7 @@ export * from './LimsMetadata'; export * from './LimsSpecificMetadata'; export * from './MetadataReloadRequest'; export * from './MetadataReloadResponse'; +export * from './NearestNeighborsRequest'; export * from './NewickTreeResponse'; export * from './Organization'; export * from './PageOfAnalysis'; diff --git a/docker-compose.yml b/docker-compose.yml index 288d357d..22146011 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: command: ["/bin/sh", "-c", "/app/start.sh"] depends_on: - "hydra" + - "bio_api" restart: on-failure environment: @@ -238,6 +239,17 @@ services: source: ./auth/config/kratos target: /etc/config/kratos + bio_api: + image: ghcr.io/ssi-dk/bio_api:latest + user: ${CURRENT_UID} + ports: + - "8000:8000" + expose: + - 8000 + environment: + BIO_API_MONGO_CONNECTION: ${BIFROST_MONGO_CONN} + restart: on-failure + networks: default: driver: bridge diff --git a/openapi_specs/SOFI/SOFI.yaml b/openapi_specs/SOFI/SOFI.yaml index a883d118..9a0484e3 100644 --- a/openapi_specs/SOFI/SOFI.yaml +++ b/openapi_specs/SOFI/SOFI.yaml @@ -14,6 +14,27 @@ security: - jwt: [] paths: + /nearest_neighbors: + post: + description: Nearest Neighbors + operationId: post + x-openapi-router-controller: web.src.SAP.generated.controllers.nearest_neighbors_controller + x-codegen-request-body-name: body + tags: + - nearest_neighbors + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/NearestNeighborsRequest" + required: true + responses: + '200': + description: Nearest Neighbors Response + content: + application/json: + schema: + "$ref": "#/components/schemas/BioApiJobResponse" /me: get: description: Describe the current user and their permissions @@ -1275,6 +1296,40 @@ components: items: $ref: "#/components/schemas/Permission" + NearestNeighborsRequest: + properties: + id: + type: string + title: Input Mongo Id + cutoff: + type: integer + title: Cutoff + type: object + required: + - id + - cutoff + title: NearestNeighborsRequest + description: Parameters for a REST request for a nearest neighbors calculation. + + BioApiJobResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/BioApiStatus" + + BioApiStatus: + type: string + enum: + - init + - completed + - error + title: Status + securitySchemes: jwt: type: http diff --git a/openapi_specs/bio_api.yaml b/openapi_specs/bio_api.yaml new file mode 100644 index 00000000..4cc28d01 --- /dev/null +++ b/openapi_specs/bio_api.yaml @@ -0,0 +1,575 @@ +openapi: 3.0.0 +info: + title: Bio API + description: REST API for controlling bioinformatic calculations + version: 0.2.0 +servers: + - url: 'http://localhost:8000/' +paths: + "/v1/nearest_neighbors": + post: + tags: + - Nearest Neighbors + summary: Nearest Neighbors + operationId: nearest_neighbors_v1_nearest_neighbors_post + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/NearestNeighborsRequest" + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/CommonPOSTResponse" + '400': + description: Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '404': + description: Not Found + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/nearest_neighbors/{nn_id}": + get: + tags: + - Nearest Neighbors + summary: Nn Result + description: Get result of a nearest neighbors calculation + operationId: nn_result_v1_nearest_neighbors__nn_id__get + parameters: + - name: nn_id + in: path + required: true + schema: + type: string + title: Nn Id + - name: level + in: query + required: false + schema: + type: string + default: full + title: Level + responses: + '200': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/NearestNeighborsGETResponse" + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Bad Request + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/distance_calculations": + post: + tags: + - Distances + summary: Dmx From Mongodb + description: Run a distance calculation from selected cgMLST profiles in MongoDB + operationId: dmx_from_mongodb_v1_distance_calculations_post + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/DistanceMatrixRequest" + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/CommonPOSTResponse" + '400': + description: Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '404': + description: Not Found + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/distance_calculations/{dc_id}": + get: + tags: + - Distances + summary: Dmx Result + description: Get result of a distance calculation + operationId: dmx_result_v1_distance_calculations__dc_id__get + parameters: + - name: dc_id + in: path + required: true + schema: + type: string + title: Dc Id + - name: level + in: query + required: false + schema: + type: string + default: full + title: Level + responses: + '200': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/DistanceMatrixGETResponse" + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Bad Request + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/trees": + post: + tags: + - Trees + summary: Hc Tree From Dmx Job + operationId: hc_tree_from_dmx_job_v1_trees_post + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/HCTreeCalcRequest" + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/CommonPOSTResponse" + '400': + description: Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '404': + description: Not Found + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/trees/{tc_id}": + get: + tags: + - Trees + summary: Hc Tree Result + operationId: hc_tree_result_v1_trees__tc_id__get + parameters: + - name: tc_id + in: path + required: true + schema: + type: string + title: Tc Id + - name: level + in: query + required: false + schema: + type: string + default: full + title: Level + responses: + '200': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/HCTreeCalcGETResponse" + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Bad Request + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" +components: + schemas: + CommonPOSTResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + type: object + required: + - job_id + - created_at + - status + title: CommonPOSTResponse + description: Common response base class for all calculation responses (both + POST and GET) + DistanceMatrixGETResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At + seq_collection: + type: string + title: Seq Collection + seqid_field_path: + type: string + title: Seqid Field Path + profile_field_path: + type: string + title: Profile Field Path + seq_mongo_ids: + items: {} + type: array + title: Seq Mongo Ids + result: + "$ref": "#/components/schemas/DistanceMatrixResult" + type: object + required: + - job_id + - created_at + - status + - finished_at + - seq_collection + - seqid_field_path + - profile_field_path + - seq_mongo_ids + - result + title: DistanceMatrixGETResponse + DistanceMatrixRequest: + properties: + seq_collection: + type: string + title: Seq Collection + seqid_field_path: + type: string + title: Seqid Field Path + profile_field_path: + type: string + title: Profile Field Path + seq_mongo_ids: + items: {} + type: array + title: Seq Mongo Ids + type: object + required: + - seq_collection + - seqid_field_path + - profile_field_path + - seq_mongo_ids + title: DistanceMatrixRequest + description: |- + Parameters for a REST request for a distance calculation. + + seq_collection: collection to find sequences in + seqid_field_path: field path in dotted notation which contains the 'sequence id' the user wants to see + profile_field_path: field path in dotted notation which contains the cgMLST allele profiles + mongo_ids: the _id strings for the desired sequence documents + DistanceMatrixResult: + properties: + seq_to_mongo: + type: object + title: Seq To Mongo + distances: + anyOf: + - type: object + - type: 'null' + title: Distances + type: object + required: + - seq_to_mongo + title: DistanceMatrixResult + HCTreeCalcGETResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At + dmx_job: + type: string + title: Dmx Job + method: + type: string + enum: + - single + - complete + - average + - weighted + - centroid + - median + - ward + title: Method + result: + anyOf: + - type: string + - type: 'null' + title: Result + type: object + required: + - job_id + - created_at + - status + - finished_at + - dmx_job + - method + - result + title: HCTreeCalcGETResponse + HCTreeCalcRequest: + properties: + dmx_job: + type: string + title: Dmx Job + method: + type: string + enum: + - single + - complete + - average + - weighted + - centroid + - median + - ward + title: Method + type: object + required: + - dmx_job + - method + title: HCTreeCalcRequest + description: |- + Parameters for a REST request for a tree calculation based on hierarchical clustering. + Distances are taken directly from the request. + HTTPValidationError: + properties: + detail: + items: + "$ref": "#/components/schemas/ValidationError" + type: array + title: Detail + type: object + title: HTTPValidationError + Message: + properties: + detail: + type: string + title: Detail + type: object + required: + - detail + title: Message + NearestNeighborsGETResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At + seq_collection: + type: string + title: Seq Collection + filtering: + anyOf: + - type: object + - type: 'null' + title: Filtering + profile_field_path: + type: string + title: Profile Field Path + input_mongo_id: + type: string + title: Input Mongo Id + cutoff: + type: integer + title: Cutoff + unknowns_are_diffs: + type: boolean + title: Unknowns Are Diffs + result: + anyOf: + - items: + "$ref": "#/components/schemas/Neighbor" + type: array + - type: 'null' + title: Result + type: object + required: + - job_id + - created_at + - status + - finished_at + - seq_collection + - profile_field_path + - input_mongo_id + - cutoff + - unknowns_are_diffs + - result + title: NearestNeighborsGETResponse + NearestNeighborsRequest: + properties: + seq_collection: + type: string + title: Seq Collection + filtering: + anyOf: + - type: object + - type: 'null' + title: Filtering + profile_field_path: + type: string + title: Profile Field Path + input_mongo_id: + type: string + title: Input Mongo Id + cutoff: + type: integer + title: Cutoff + unknowns_are_diffs: + type: boolean + title: Unknowns Are Diffs + type: object + required: + - seq_collection + - profile_field_path + - input_mongo_id + - cutoff + - unknowns_are_diffs + title: NearestNeighborsRequest + description: Parameters for a REST request for a nearest neighbors calculation. + Neighbor: + properties: + id: + type: string + title: Id + diff_count: + type: integer + title: Diff Count + type: object + required: + - id + - diff_count + title: Neighbor + Status: + type: string + enum: + - init + - completed + - error + title: Status + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + type: object + required: + - loc + - msg + - type + title: ValidationError diff --git a/web/openapi_specs/SOFI/SOFI.yaml b/web/openapi_specs/SOFI/SOFI.yaml index a883d118..9a0484e3 100644 --- a/web/openapi_specs/SOFI/SOFI.yaml +++ b/web/openapi_specs/SOFI/SOFI.yaml @@ -14,6 +14,27 @@ security: - jwt: [] paths: + /nearest_neighbors: + post: + description: Nearest Neighbors + operationId: post + x-openapi-router-controller: web.src.SAP.generated.controllers.nearest_neighbors_controller + x-codegen-request-body-name: body + tags: + - nearest_neighbors + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/NearestNeighborsRequest" + required: true + responses: + '200': + description: Nearest Neighbors Response + content: + application/json: + schema: + "$ref": "#/components/schemas/BioApiJobResponse" /me: get: description: Describe the current user and their permissions @@ -1275,6 +1296,40 @@ components: items: $ref: "#/components/schemas/Permission" + NearestNeighborsRequest: + properties: + id: + type: string + title: Input Mongo Id + cutoff: + type: integer + title: Cutoff + type: object + required: + - id + - cutoff + title: NearestNeighborsRequest + description: Parameters for a REST request for a nearest neighbors calculation. + + BioApiJobResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/BioApiStatus" + + BioApiStatus: + type: string + enum: + - init + - completed + - error + title: Status + securitySchemes: jwt: type: http diff --git a/web/openapi_specs/bio_api.yaml b/web/openapi_specs/bio_api.yaml new file mode 100644 index 00000000..11e58376 --- /dev/null +++ b/web/openapi_specs/bio_api.yaml @@ -0,0 +1,575 @@ +openapi: 3.0.0 +info: + title: Bio API + description: REST API for controlling bioinformatic calculations1 + version: 0.2.0 +servers: + - url: 'http://localhost:8000/' +paths: + "/v1/nearest_neighbors": + post: + tags: + - Nearest Neighbors + summary: Nearest Neighbors + operationId: nearest_neighbors_v1_nearest_neighbors_post + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/NearestNeighborsRequest" + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/CommonPOSTResponse" + '400': + description: Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '404': + description: Not Found + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/nearest_neighbors/{nn_id}": + get: + tags: + - Nearest Neighbors + summary: Nn Result + description: Get result of a nearest neighbors calculation + operationId: nn_result_v1_nearest_neighbors__nn_id__get + parameters: + - name: nn_id + in: path + required: true + schema: + type: string + title: Nn Id + - name: level + in: query + required: false + schema: + type: string + default: full + title: Level + responses: + '200': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/NearestNeighborsGETResponse" + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Bad Request + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/distance_calculations": + post: + tags: + - Distances + summary: Dmx From Mongodb + description: Run a distance calculation from selected cgMLST profiles in MongoDB + operationId: dmx_from_mongodb_v1_distance_calculations_post + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/DistanceMatrixRequest" + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/CommonPOSTResponse" + '400': + description: Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '404': + description: Not Found + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/distance_calculations/{dc_id}": + get: + tags: + - Distances + summary: Dmx Result + description: Get result of a distance calculation + operationId: dmx_result_v1_distance_calculations__dc_id__get + parameters: + - name: dc_id + in: path + required: true + schema: + type: string + title: Dc Id + - name: level + in: query + required: false + schema: + type: string + default: full + title: Level + responses: + '200': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/DistanceMatrixGETResponse" + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Bad Request + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/trees": + post: + tags: + - Trees + summary: Hc Tree From Dmx Job + operationId: hc_tree_from_dmx_job_v1_trees_post + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/HCTreeCalcRequest" + required: true + responses: + '201': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/CommonPOSTResponse" + '400': + description: Bad Request + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '404': + description: Not Found + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" + "/v1/trees/{tc_id}": + get: + tags: + - Trees + summary: Hc Tree Result + operationId: hc_tree_result_v1_trees__tc_id__get + parameters: + - name: tc_id + in: path + required: true + schema: + type: string + title: Tc Id + - name: level + in: query + required: false + schema: + type: string + default: full + title: Level + responses: + '200': + description: Successful Response + content: + application/json: + schema: + "$ref": "#/components/schemas/HCTreeCalcGETResponse" + '400': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Bad Request + '404': + content: + application/json: + schema: + "$ref": "#/components/schemas/Message" + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + "$ref": "#/components/schemas/HTTPValidationError" +components: + schemas: + CommonPOSTResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + type: object + required: + - job_id + - created_at + - status + title: CommonPOSTResponse + description: Common response base class for all calculation responses (both + POST and GET) + DistanceMatrixGETResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At + seq_collection: + type: string + title: Seq Collection + seqid_field_path: + type: string + title: Seqid Field Path + profile_field_path: + type: string + title: Profile Field Path + seq_mongo_ids: + items: {} + type: array + title: Seq Mongo Ids + result: + "$ref": "#/components/schemas/DistanceMatrixResult" + type: object + required: + - job_id + - created_at + - status + - finished_at + - seq_collection + - seqid_field_path + - profile_field_path + - seq_mongo_ids + - result + title: DistanceMatrixGETResponse + DistanceMatrixRequest: + properties: + seq_collection: + type: string + title: Seq Collection + seqid_field_path: + type: string + title: Seqid Field Path + profile_field_path: + type: string + title: Profile Field Path + seq_mongo_ids: + items: {} + type: array + title: Seq Mongo Ids + type: object + required: + - seq_collection + - seqid_field_path + - profile_field_path + - seq_mongo_ids + title: DistanceMatrixRequest + description: |- + Parameters for a REST request for a distance calculation. + + seq_collection: collection to find sequences in + seqid_field_path: field path in dotted notation which contains the 'sequence id' the user wants to see + profile_field_path: field path in dotted notation which contains the cgMLST allele profiles + mongo_ids: the _id strings for the desired sequence documents + DistanceMatrixResult: + properties: + seq_to_mongo: + type: object + title: Seq To Mongo + distances: + anyOf: + - type: object + - type: 'null' + title: Distances + type: object + required: + - seq_to_mongo + title: DistanceMatrixResult + HCTreeCalcGETResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At + dmx_job: + type: string + title: Dmx Job + method: + type: string + enum: + - single + - complete + - average + - weighted + - centroid + - median + - ward + title: Method + result: + anyOf: + - type: string + - type: 'null' + title: Result + type: object + required: + - job_id + - created_at + - status + - finished_at + - dmx_job + - method + - result + title: HCTreeCalcGETResponse + HCTreeCalcRequest: + properties: + dmx_job: + type: string + title: Dmx Job + method: + type: string + enum: + - single + - complete + - average + - weighted + - centroid + - median + - ward + title: Method + type: object + required: + - dmx_job + - method + title: HCTreeCalcRequest + description: |- + Parameters for a REST request for a tree calculation based on hierarchical clustering. + Distances are taken directly from the request. + HTTPValidationError: + properties: + detail: + items: + "$ref": "#/components/schemas/ValidationError" + type: array + title: Detail + type: object + title: HTTPValidationError + Message: + properties: + detail: + type: string + title: Detail + type: object + required: + - detail + title: Message + NearestNeighborsGETResponse: + properties: + job_id: + type: string + title: Job Id + created_at: + type: string + title: Created At + status: + "$ref": "#/components/schemas/Status" + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At + seq_collection: + type: string + title: Seq Collection + filtering: + anyOf: + - type: object + - type: 'null' + title: Filtering + profile_field_path: + type: string + title: Profile Field Path + input_mongo_id: + type: string + title: Input Mongo Id + cutoff: + type: integer + title: Cutoff + unknowns_are_diffs: + type: boolean + title: Unknowns Are Diffs + result: + anyOf: + - items: + "$ref": "#/components/schemas/Neighbor" + type: array + - type: 'null' + title: Result + type: object + required: + - job_id + - created_at + - status + - finished_at + - seq_collection + - profile_field_path + - input_mongo_id + - cutoff + - unknowns_are_diffs + - result + title: NearestNeighborsGETResponse + NearestNeighborsRequest: + properties: + seq_collection: + type: string + title: Seq Collection + filtering: + anyOf: + - type: object + - type: 'null' + title: Filtering + profile_field_path: + type: string + title: Profile Field Path + input_mongo_id: + type: string + title: Input Mongo Id + cutoff: + type: integer + title: Cutoff + unknowns_are_diffs: + type: boolean + title: Unknowns Are Diffs + type: object + required: + - seq_collection + - profile_field_path + - input_mongo_id + - cutoff + - unknowns_are_diffs + title: NearestNeighborsRequest + description: Parameters for a REST request for a nearest neighbors calculation. + Neighbor: + properties: + id: + type: string + title: Id + diff_count: + type: integer + title: Diff Count + type: object + required: + - id + - diff_count + title: Neighbor + Status: + type: string + enum: + - init + - completed + - error + title: Status + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + type: object + required: + - loc + - msg + - type + title: ValidationError diff --git a/web/src/SAP/generated/controllers/nearest_neighbors_controller.py b/web/src/SAP/generated/controllers/nearest_neighbors_controller.py new file mode 100644 index 00000000..03edf485 --- /dev/null +++ b/web/src/SAP/generated/controllers/nearest_neighbors_controller.py @@ -0,0 +1,20 @@ +import connexion +import six + +from .. import util +from ...src.controllers import NearestNeighborsController + +def post(user, token_info, body): # noqa: E501 + """post + + Nearest Neighbors # noqa: E501 + + :param body: + :type body: dict | bytes + + :rtype: BioApiJobResponse + """ + if connexion.request.is_json: + from ..models import NearestNeighborsRequest + body = NearestNeighborsRequest.from_dict(connexion.request.get_json()) # noqa: E501 + return NearestNeighborsController.post(user, token_info, body) diff --git a/web/src/SAP/generated/models/__init__.py b/web/src/SAP/generated/models/__init__.py index 15466c29..625ae4da 100644 --- a/web/src/SAP/generated/models/__init__.py +++ b/web/src/SAP/generated/models/__init__.py @@ -12,6 +12,8 @@ from .approval_request import ApprovalRequest from .approval_status import ApprovalStatus from .base_metadata import BaseMetadata +from .bio_api_job_response import BioApiJobResponse +from .bio_api_status import BioApiStatus from .bulk_upload_request import BulkUploadRequest from .column import Column from .data_clearance import DataClearance @@ -24,6 +26,7 @@ from .metadata_reload_request import MetadataReloadRequest from .metadata_reload_response import MetadataReloadResponse from .multi_upload_request import MultiUploadRequest +from .nearest_neighbors_request import NearestNeighborsRequest from .newick_tree_response import NewickTreeResponse from .organization import Organization from .page_of_analysis import PageOfAnalysis diff --git a/web/src/SAP/generated/models/bio_api_job_response.py b/web/src/SAP/generated/models/bio_api_job_response.py new file mode 100644 index 00000000..22641edc --- /dev/null +++ b/web/src/SAP/generated/models/bio_api_job_response.py @@ -0,0 +1,120 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from web.src.SAP.generated.models.base_model_ import Model +from web.src.SAP.generated.models.bio_api_status import BioApiStatus +from web.src.SAP.generated import util + +from web.src.SAP.generated.models.bio_api_status import BioApiStatus # noqa: E501 + +class BioApiJobResponse(Model): + + + + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + Do not edit the class manually. + """ + + def __init__(self, job_id=None, created_at=None, status=None): # noqa: E501 + """BioApiJobResponse - a model defined in OpenAPI + + :param job_id: The job_id of this BioApiJobResponse. # noqa: E501 + :type job_id: str + :param created_at: The created_at of this BioApiJobResponse. # noqa: E501 + :type created_at: str + :param status: The status of this BioApiJobResponse. # noqa: E501 + :type status: BioApiStatus + """ + self.openapi_types = { + 'job_id': str, + 'created_at': str, + 'status': BioApiStatus, + } + + self.attribute_map = { + 'job_id': 'job_id', + 'created_at': 'created_at', + 'status': 'status', + } + + self._job_id = job_id + self._created_at = created_at + self._status = status + + @classmethod + def from_dict(cls, dikt): + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The BioApiJobResponse of this BioApiJobResponse. # noqa: E501 + :rtype: BioApiJobResponse + """ + return util.deserialize_model(dikt, cls) + + @property + def job_id(self): + """Gets the job_id of this BioApiJobResponse. + + + :return: The job_id of this BioApiJobResponse. + :rtype: str + """ + return self._job_id + + @job_id.setter + def job_id(self, job_id): + """Sets the job_id of this BioApiJobResponse. + + + :param job_id: The job_id of this BioApiJobResponse. + :type job_id: str + """ + + self._job_id = job_id + + @property + def created_at(self): + """Gets the created_at of this BioApiJobResponse. + + + :return: The created_at of this BioApiJobResponse. + :rtype: str + """ + return self._created_at + + @created_at.setter + def created_at(self, created_at): + """Sets the created_at of this BioApiJobResponse. + + + :param created_at: The created_at of this BioApiJobResponse. + :type created_at: str + """ + + self._created_at = created_at + + @property + def status(self): + """Gets the status of this BioApiJobResponse. + + + :return: The status of this BioApiJobResponse. + :rtype: BioApiStatus + """ + return self._status + + @status.setter + def status(self, status): + """Sets the status of this BioApiJobResponse. + + + :param status: The status of this BioApiJobResponse. + :type status: BioApiStatus + """ + + self._status = status diff --git a/web/src/SAP/generated/models/bio_api_status.py b/web/src/SAP/generated/models/bio_api_status.py new file mode 100644 index 00000000..fd95ef1c --- /dev/null +++ b/web/src/SAP/generated/models/bio_api_status.py @@ -0,0 +1,45 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from web.src.SAP.generated.models.base_model_ import Model +from web.src.SAP.generated import util + + +class BioApiStatus(Model): + + + + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + Do not edit the class manually. + """ + + """ + allowed enum values + """ + INIT = "init" + COMPLETED = "completed" + ERROR = "error" + def __init__(self): # noqa: E501 + """BioApiStatus - a model defined in OpenAPI + + """ + self.openapi_types = { + } + + self.attribute_map = { + } + + @classmethod + def from_dict(cls, dikt): + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The BioApiStatus of this BioApiStatus. # noqa: E501 + :rtype: BioApiStatus + """ + return util.deserialize_model(dikt, cls) diff --git a/web/src/SAP/generated/models/nearest_neighbors_request.py b/web/src/SAP/generated/models/nearest_neighbors_request.py new file mode 100644 index 00000000..96fa4a09 --- /dev/null +++ b/web/src/SAP/generated/models/nearest_neighbors_request.py @@ -0,0 +1,96 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from web.src.SAP.generated.models.base_model_ import Model +from web.src.SAP.generated import util + + +class NearestNeighborsRequest(Model): + + + + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + Do not edit the class manually. + """ + + def __init__(self, id=None, cutoff=None): # noqa: E501 + """NearestNeighborsRequest - a model defined in OpenAPI + + :param id: The id of this NearestNeighborsRequest. # noqa: E501 + :type id: str + :param cutoff: The cutoff of this NearestNeighborsRequest. # noqa: E501 + :type cutoff: int + """ + self.openapi_types = { + 'id': str, + 'cutoff': int, + } + + self.attribute_map = { + 'id': 'id', + 'cutoff': 'cutoff', + } + + self._id = id + self._cutoff = cutoff + + @classmethod + def from_dict(cls, dikt): + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The NearestNeighborsRequest of this NearestNeighborsRequest. # noqa: E501 + :rtype: NearestNeighborsRequest + """ + return util.deserialize_model(dikt, cls) + + @property + def id(self): + """Gets the id of this NearestNeighborsRequest. + + + :return: The id of this NearestNeighborsRequest. + :rtype: str + """ + return self._id + + @id.setter + def id(self, id): + """Sets the id of this NearestNeighborsRequest. + + + :param id: The id of this NearestNeighborsRequest. + :type id: str + """ + if id is None: + raise ValueError("Invalid value for `id`, must not be `None`") # noqa: E501 + + self._id = id + + @property + def cutoff(self): + """Gets the cutoff of this NearestNeighborsRequest. + + + :return: The cutoff of this NearestNeighborsRequest. + :rtype: int + """ + return self._cutoff + + @cutoff.setter + def cutoff(self, cutoff): + """Sets the cutoff of this NearestNeighborsRequest. + + + :param cutoff: The cutoff of this NearestNeighborsRequest. + :type cutoff: int + """ + if cutoff is None: + raise ValueError("Invalid value for `cutoff`, must not be `None`") # noqa: E501 + + self._cutoff = cutoff diff --git a/web/src/SAP/generated/openapi/openapi.yaml b/web/src/SAP/generated/openapi/openapi.yaml index 5194d405..e626efd0 100644 --- a/web/src/SAP/generated/openapi/openapi.yaml +++ b/web/src/SAP/generated/openapi/openapi.yaml @@ -341,6 +341,27 @@ paths: tags: - user x-openapi-router-controller: web.src.SAP.generated.controllers.user_controller + /nearest_neighbors: + post: + description: Nearest Neighbors + operationId: post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NearestNeighborsRequest' + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/BioApiJobResponse' + description: Nearest Neighbors Response + tags: + - nearest_neighbors + x-openapi-router-controller: web.src.SAP.generated.controllers.nearest_neighbors_controller + x-codegen-request-body-name: body /samples/{sequence_id}: get: description: Get an individual sample by id @@ -1245,6 +1266,43 @@ components: $ref: '#/components/schemas/Permission' type: array type: object + NearestNeighborsRequest: + description: Parameters for a REST request for a nearest neighbors calculation. + example: + id: id + cutoff: 0 + properties: + id: + title: Input Mongo Id + type: string + cutoff: + title: Cutoff + type: integer + required: + - cutoff + - id + title: NearestNeighborsRequest + type: object + BioApiJobResponse: + example: + job_id: job_id + created_at: created_at + properties: + job_id: + title: Job Id + type: string + created_at: + title: Created At + type: string + status: + $ref: '#/components/schemas/BioApiStatus' + BioApiStatus: + enum: + - init + - completed + - error + title: Status + type: string Sample_categories_resistance_report: example: phenotypes: diff --git a/web/src/SAP/generated/test/test_nearest_neighbors_controller.py b/web/src/SAP/generated/test/test_nearest_neighbors_controller.py new file mode 100644 index 00000000..869e9b77 --- /dev/null +++ b/web/src/SAP/generated/test/test_nearest_neighbors_controller.py @@ -0,0 +1,41 @@ +# coding: utf-8 + +import unittest + +from flask import json +from six import BytesIO + +from web.src.SAP.generated.models.bio_api_job_response import BioApiJobResponse # noqa: E501 +from web.src.SAP.generated.models.nearest_neighbors_request import NearestNeighborsRequest # noqa: E501 +from .test import BaseTestCase + + +class TestNearestNeighborsController(BaseTestCase): + """NearestNeighborsController integration test stubs""" + + def test_post(self): + """Test case for post + + + """ + body = { + "id" : "id", + "cutoff" : 0 +} + headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'Bearer special-key', + } + response = self.client.open( + '/api/nearest_neighbors', + method='POST', + headers=headers, + data=json.dumps(body), + content_type='application/json') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + unittest.main() diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py new file mode 100644 index 00000000..4ecddbec --- /dev/null +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -0,0 +1,17 @@ +from web.src.SAP.generated.models.nearest_neighbors_request import NearestNeighborsRequest +from web.src.services.bio_api.openapi.api.nearest_neighbors_api import NearestNeighborsApi +from web.src.services.bio_api.openapi.api_client import ApiClient +from web.src.services.bio_api.openapi.configuration import Configuration +from web.src.services.bio_api.openapi.model.nearest_neighbors_request import NearestNeighborsRequest as BioNearestNeighborsRequest + +from flask.json import jsonify + +def post(user, token, body: NearestNeighborsRequest): + with ApiClient(Configuration(host="http://bio_api:8000")) as api_client: + api_instance = NearestNeighborsApi(api_client) + + # TODO: filtering={"species_final": "value"} + request = BioNearestNeighborsRequest("sap_analysis_results", "st_alleles", body.id, body.cutoff, False) + api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) + + return jsonify(api_response.to_dict()) diff --git a/web/src/services/__init__.py b/web/src/services/__init__.py index 9088b1ee..99a60af1 100644 --- a/web/src/services/__init__.py +++ b/web/src/services/__init__.py @@ -1 +1 @@ -from web.src.services.lims import Lims, Connection, Isolate, LimsApi +# from web.src.services.lims import Lims, Connection, Isolate, LimsApi diff --git a/web/src/services/bio_api/__init__.py b/web/src/services/bio_api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web/src/services/bio_api/openapi/__init__.py b/web/src/services/bio_api/openapi/__init__.py new file mode 100644 index 00000000..77999a41 --- /dev/null +++ b/web/src/services/bio_api/openapi/__init__.py @@ -0,0 +1,27 @@ +# flake8: noqa + +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +__version__ = "1.0.0" + +# import ApiClient +from web.src.services.bio_api.openapi.api_client import ApiClient + +# import Configuration +from web.src.services.bio_api.openapi.configuration import Configuration + +# import exceptions +from web.src.services.bio_api.openapi.exceptions import OpenApiException +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError +from web.src.services.bio_api.openapi.exceptions import ApiTypeError +from web.src.services.bio_api.openapi.exceptions import ApiValueError +from web.src.services.bio_api.openapi.exceptions import ApiKeyError +from web.src.services.bio_api.openapi.exceptions import ApiException diff --git a/web/src/services/bio_api/openapi/api/__init__.py b/web/src/services/bio_api/openapi/api/__init__.py new file mode 100644 index 00000000..46c6a4cf --- /dev/null +++ b/web/src/services/bio_api/openapi/api/__init__.py @@ -0,0 +1,3 @@ +# do not import all apis into this module because that uses a lot of memory and stack frames +# if you need the ability to import all apis from one package, import them with +# from web.src.services.bio_api.openapi.apis import DistancesApi diff --git a/web/src/services/bio_api/openapi/api/distances_api.py b/web/src/services/bio_api/openapi/api/distances_api.py new file mode 100644 index 00000000..74716757 --- /dev/null +++ b/web/src/services/bio_api/openapi/api/distances_api.py @@ -0,0 +1,282 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.api_client import ApiClient, Endpoint as _Endpoint +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + check_allowed_values, + check_validations, + date, + datetime, + file_type, + none_type, + validate_and_convert_types +) +from web.src.services.bio_api.openapi.model.common_post_response import CommonPOSTResponse +from web.src.services.bio_api.openapi.model.distance_matrix_get_response import DistanceMatrixGETResponse +from web.src.services.bio_api.openapi.model.distance_matrix_request import DistanceMatrixRequest +from web.src.services.bio_api.openapi.model.http_validation_error import HTTPValidationError +from web.src.services.bio_api.openapi.model.message import Message + + +class DistancesApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def __dmx_from_mongodb_v1_distance_calculations_post( + self, + distance_matrix_request, + **kwargs + ): + """Dmx From Mongodb # noqa: E501 + + Run a distance calculation from selected cgMLST profiles in MongoDB # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.dmx_from_mongodb_v1_distance_calculations_post(distance_matrix_request, async_req=True) + >>> result = thread.get() + + Args: + distance_matrix_request (DistanceMatrixRequest): + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + CommonPOSTResponse + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index') + kwargs['distance_matrix_request'] = \ + distance_matrix_request + return self.call_with_http_info(**kwargs) + + self.dmx_from_mongodb_v1_distance_calculations_post = _Endpoint( + settings={ + 'response_type': (CommonPOSTResponse,), + 'auth': [], + 'endpoint_path': '/v1/distance_calculations', + 'operation_id': 'dmx_from_mongodb_v1_distance_calculations_post', + 'http_method': 'POST', + 'servers': None, + }, + params_map={ + 'all': [ + 'distance_matrix_request', + ], + 'required': [ + 'distance_matrix_request', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'distance_matrix_request': + (DistanceMatrixRequest,), + }, + 'attribute_map': { + }, + 'location_map': { + 'distance_matrix_request': 'body', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [ + 'application/json' + ] + }, + api_client=api_client, + callable=__dmx_from_mongodb_v1_distance_calculations_post + ) + + def __dmx_result_v1_distance_calculations_dc_id_get( + self, + dc_id, + **kwargs + ): + """Dmx Result # noqa: E501 + + Get result of a distance calculation # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.dmx_result_v1_distance_calculations_dc_id_get(dc_id, async_req=True) + >>> result = thread.get() + + Args: + dc_id (str): + + Keyword Args: + level (str): [optional] if omitted the server will use the default value of "full" + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + DistanceMatrixGETResponse + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index') + kwargs['dc_id'] = \ + dc_id + return self.call_with_http_info(**kwargs) + + self.dmx_result_v1_distance_calculations_dc_id_get = _Endpoint( + settings={ + 'response_type': (DistanceMatrixGETResponse,), + 'auth': [], + 'endpoint_path': '/v1/distance_calculations/{dc_id}', + 'operation_id': 'dmx_result_v1_distance_calculations_dc_id_get', + 'http_method': 'GET', + 'servers': None, + }, + params_map={ + 'all': [ + 'dc_id', + 'level', + ], + 'required': [ + 'dc_id', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'dc_id': + (str,), + 'level': + (str,), + }, + 'attribute_map': { + 'dc_id': 'dc_id', + 'level': 'level', + }, + 'location_map': { + 'dc_id': 'path', + 'level': 'query', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__dmx_result_v1_distance_calculations_dc_id_get + ) diff --git a/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py b/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py new file mode 100644 index 00000000..40710cac --- /dev/null +++ b/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py @@ -0,0 +1,281 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.api_client import ApiClient, Endpoint as _Endpoint +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + check_allowed_values, + check_validations, + date, + datetime, + file_type, + none_type, + validate_and_convert_types +) +from web.src.services.bio_api.openapi.model.common_post_response import CommonPOSTResponse +from web.src.services.bio_api.openapi.model.http_validation_error import HTTPValidationError +from web.src.services.bio_api.openapi.model.message import Message +from web.src.services.bio_api.openapi.model.nearest_neighbors_get_response import NearestNeighborsGETResponse +from web.src.services.bio_api.openapi.model.nearest_neighbors_request import NearestNeighborsRequest + + +class NearestNeighborsApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def __nearest_neighbors_v1_nearest_neighbors_post( + self, + nearest_neighbors_request, + **kwargs + ): + """Nearest Neighbors # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.nearest_neighbors_v1_nearest_neighbors_post(nearest_neighbors_request, async_req=True) + >>> result = thread.get() + + Args: + nearest_neighbors_request (NearestNeighborsRequest): + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + CommonPOSTResponse + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index') + kwargs['nearest_neighbors_request'] = \ + nearest_neighbors_request + return self.call_with_http_info(**kwargs) + + self.nearest_neighbors_v1_nearest_neighbors_post = _Endpoint( + settings={ + 'response_type': (CommonPOSTResponse,), + 'auth': [], + 'endpoint_path': '/v1/nearest_neighbors', + 'operation_id': 'nearest_neighbors_v1_nearest_neighbors_post', + 'http_method': 'POST', + 'servers': None, + }, + params_map={ + 'all': [ + 'nearest_neighbors_request', + ], + 'required': [ + 'nearest_neighbors_request', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'nearest_neighbors_request': + (NearestNeighborsRequest,), + }, + 'attribute_map': { + }, + 'location_map': { + 'nearest_neighbors_request': 'body', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [ + 'application/json' + ] + }, + api_client=api_client, + callable=__nearest_neighbors_v1_nearest_neighbors_post + ) + + def __nn_result_v1_nearest_neighbors_nn_id_get( + self, + nn_id, + **kwargs + ): + """Nn Result # noqa: E501 + + Get result of a nearest neighbors calculation # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.nn_result_v1_nearest_neighbors_nn_id_get(nn_id, async_req=True) + >>> result = thread.get() + + Args: + nn_id (str): + + Keyword Args: + level (str): [optional] if omitted the server will use the default value of "full" + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + NearestNeighborsGETResponse + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index') + kwargs['nn_id'] = \ + nn_id + return self.call_with_http_info(**kwargs) + + self.nn_result_v1_nearest_neighbors_nn_id_get = _Endpoint( + settings={ + 'response_type': (NearestNeighborsGETResponse,), + 'auth': [], + 'endpoint_path': '/v1/nearest_neighbors/{nn_id}', + 'operation_id': 'nn_result_v1_nearest_neighbors_nn_id_get', + 'http_method': 'GET', + 'servers': None, + }, + params_map={ + 'all': [ + 'nn_id', + 'level', + ], + 'required': [ + 'nn_id', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'nn_id': + (str,), + 'level': + (str,), + }, + 'attribute_map': { + 'nn_id': 'nn_id', + 'level': 'level', + }, + 'location_map': { + 'nn_id': 'path', + 'level': 'query', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__nn_result_v1_nearest_neighbors_nn_id_get + ) diff --git a/web/src/services/bio_api/openapi/api/trees_api.py b/web/src/services/bio_api/openapi/api/trees_api.py new file mode 100644 index 00000000..427096a5 --- /dev/null +++ b/web/src/services/bio_api/openapi/api/trees_api.py @@ -0,0 +1,280 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.api_client import ApiClient, Endpoint as _Endpoint +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + check_allowed_values, + check_validations, + date, + datetime, + file_type, + none_type, + validate_and_convert_types +) +from web.src.services.bio_api.openapi.model.common_post_response import CommonPOSTResponse +from web.src.services.bio_api.openapi.model.hc_tree_calc_get_response import HCTreeCalcGETResponse +from web.src.services.bio_api.openapi.model.hc_tree_calc_request import HCTreeCalcRequest +from web.src.services.bio_api.openapi.model.http_validation_error import HTTPValidationError +from web.src.services.bio_api.openapi.model.message import Message + + +class TreesApi(object): + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def __hc_tree_from_dmx_job_v1_trees_post( + self, + hc_tree_calc_request, + **kwargs + ): + """Hc Tree From Dmx Job # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.hc_tree_from_dmx_job_v1_trees_post(hc_tree_calc_request, async_req=True) + >>> result = thread.get() + + Args: + hc_tree_calc_request (HCTreeCalcRequest): + + Keyword Args: + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + CommonPOSTResponse + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index') + kwargs['hc_tree_calc_request'] = \ + hc_tree_calc_request + return self.call_with_http_info(**kwargs) + + self.hc_tree_from_dmx_job_v1_trees_post = _Endpoint( + settings={ + 'response_type': (CommonPOSTResponse,), + 'auth': [], + 'endpoint_path': '/v1/trees', + 'operation_id': 'hc_tree_from_dmx_job_v1_trees_post', + 'http_method': 'POST', + 'servers': None, + }, + params_map={ + 'all': [ + 'hc_tree_calc_request', + ], + 'required': [ + 'hc_tree_calc_request', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'hc_tree_calc_request': + (HCTreeCalcRequest,), + }, + 'attribute_map': { + }, + 'location_map': { + 'hc_tree_calc_request': 'body', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [ + 'application/json' + ] + }, + api_client=api_client, + callable=__hc_tree_from_dmx_job_v1_trees_post + ) + + def __hc_tree_result_v1_trees_tc_id_get( + self, + tc_id, + **kwargs + ): + """Hc Tree Result # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.hc_tree_result_v1_trees_tc_id_get(tc_id, async_req=True) + >>> result = thread.get() + + Args: + tc_id (str): + + Keyword Args: + level (str): [optional] if omitted the server will use the default value of "full" + _return_http_data_only (bool): response data without head status + code and headers. Default is True. + _preload_content (bool): if False, the urllib3.HTTPResponse object + will be returned without reading/decoding response data. + Default is True. + _request_timeout (int/float/tuple): timeout setting for this request. If + one number provided, it will be total request timeout. It can also + be a pair (tuple) of (connection, read) timeouts. + Default is None. + _check_input_type (bool): specifies if type checking + should be done one the data sent to the server. + Default is True. + _check_return_type (bool): specifies if type checking + should be done one the data received from the server. + Default is True. + _host_index (int/None): specifies the index of the server + that we want to use. + Default is read from the configuration. + async_req (bool): execute request asynchronously + + Returns: + HCTreeCalcGETResponse + If the method is called asynchronously, returns the request + thread. + """ + kwargs['async_req'] = kwargs.get( + 'async_req', False + ) + kwargs['_return_http_data_only'] = kwargs.get( + '_return_http_data_only', True + ) + kwargs['_preload_content'] = kwargs.get( + '_preload_content', True + ) + kwargs['_request_timeout'] = kwargs.get( + '_request_timeout', None + ) + kwargs['_check_input_type'] = kwargs.get( + '_check_input_type', True + ) + kwargs['_check_return_type'] = kwargs.get( + '_check_return_type', True + ) + kwargs['_host_index'] = kwargs.get('_host_index') + kwargs['tc_id'] = \ + tc_id + return self.call_with_http_info(**kwargs) + + self.hc_tree_result_v1_trees_tc_id_get = _Endpoint( + settings={ + 'response_type': (HCTreeCalcGETResponse,), + 'auth': [], + 'endpoint_path': '/v1/trees/{tc_id}', + 'operation_id': 'hc_tree_result_v1_trees_tc_id_get', + 'http_method': 'GET', + 'servers': None, + }, + params_map={ + 'all': [ + 'tc_id', + 'level', + ], + 'required': [ + 'tc_id', + ], + 'nullable': [ + ], + 'enum': [ + ], + 'validation': [ + ] + }, + root_map={ + 'validations': { + }, + 'allowed_values': { + }, + 'openapi_types': { + 'tc_id': + (str,), + 'level': + (str,), + }, + 'attribute_map': { + 'tc_id': 'tc_id', + 'level': 'level', + }, + 'location_map': { + 'tc_id': 'path', + 'level': 'query', + }, + 'collection_format_map': { + } + }, + headers_map={ + 'accept': [ + 'application/json' + ], + 'content_type': [], + }, + api_client=api_client, + callable=__hc_tree_result_v1_trees_tc_id_get + ) diff --git a/web/src/services/bio_api/openapi/api_client.py b/web/src/services/bio_api/openapi/api_client.py new file mode 100644 index 00000000..4fa4dbc2 --- /dev/null +++ b/web/src/services/bio_api/openapi/api_client.py @@ -0,0 +1,849 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import json +import atexit +import mimetypes +from multiprocessing.pool import ThreadPool +import io +import os +import re +import typing +from urllib.parse import quote +from urllib3.fields import RequestField + + +from web.src.services.bio_api.openapi import rest +from web.src.services.bio_api.openapi.configuration import Configuration +from web.src.services.bio_api.openapi.exceptions import ApiTypeError, ApiValueError, ApiException +from web.src.services.bio_api.openapi.model_utils import ( + ModelNormal, + ModelSimple, + ModelComposed, + check_allowed_values, + check_validations, + date, + datetime, + deserialize_file, + file_type, + model_to_dict, + none_type, + validate_and_convert_types +) + + +class ApiClient(object): + """Generic API client for OpenAPI client library builds. + + OpenAPI generic API client. This client handles the client- + server communication, and is invariant across implementations. Specifics of + the methods and models for each application are generated from the OpenAPI + templates. + + NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + Do not edit the class manually. + + :param configuration: .Configuration object for this client + :param header_name: a header to pass when making calls to the API. + :param header_value: a header value to pass when making calls to + the API. + :param cookie: a cookie to include in the header when making calls + to the API + :param pool_threads: The number of threads to use for async requests + to the API. More threads means more concurrent API requests. + """ + + _pool = None + + def __init__(self, configuration=None, header_name=None, header_value=None, + cookie=None, pool_threads=1): + if configuration is None: + configuration = Configuration.get_default_copy() + self.configuration = configuration + self.pool_threads = pool_threads + + self.rest_client = rest.RESTClientObject(configuration) + self.default_headers = {} + if header_name is not None: + self.default_headers[header_name] = header_value + self.cookie = cookie + # Set default User-Agent. + self.user_agent = 'OpenAPI-Generator/1.0.0/python' + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + if self._pool: + self._pool.close() + self._pool.join() + self._pool = None + if hasattr(atexit, 'unregister'): + atexit.unregister(self.close) + + @property + def pool(self): + """Create thread pool on first request + avoids instantiating unused threadpool for blocking clients. + """ + if self._pool is None: + atexit.register(self.close) + self._pool = ThreadPool(self.pool_threads) + return self._pool + + @property + def user_agent(self): + """User agent for this API client""" + return self.default_headers['User-Agent'] + + @user_agent.setter + def user_agent(self, value): + self.default_headers['User-Agent'] = value + + def set_default_header(self, header_name, header_value): + self.default_headers[header_name] = header_value + + def __call_api( + self, + resource_path: str, + method: str, + path_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + query_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, + header_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + body: typing.Optional[typing.Any] = None, + post_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, + files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None, + response_type: typing.Optional[typing.Tuple[typing.Any]] = None, + auth_settings: typing.Optional[typing.List[str]] = None, + _return_http_data_only: typing.Optional[bool] = None, + collection_formats: typing.Optional[typing.Dict[str, str]] = None, + _preload_content: bool = True, + _request_timeout: typing.Optional[typing.Union[int, float, typing.Tuple]] = None, + _host: typing.Optional[str] = None, + _check_type: typing.Optional[bool] = None + ): + + config = self.configuration + + # header parameters + header_params = header_params or {} + header_params.update(self.default_headers) + if self.cookie: + header_params['Cookie'] = self.cookie + if header_params: + header_params = self.sanitize_for_serialization(header_params) + header_params = dict(self.parameters_to_tuples(header_params, + collection_formats)) + + # path parameters + if path_params: + path_params = self.sanitize_for_serialization(path_params) + path_params = self.parameters_to_tuples(path_params, + collection_formats) + for k, v in path_params: + # specified safe chars, encode everything + resource_path = resource_path.replace( + '{%s}' % k, + quote(str(v), safe=config.safe_chars_for_path_param) + ) + + # query parameters + if query_params: + query_params = self.sanitize_for_serialization(query_params) + query_params = self.parameters_to_tuples(query_params, + collection_formats) + + # post parameters + if post_params or files: + post_params = post_params if post_params else [] + post_params = self.sanitize_for_serialization(post_params) + post_params = self.parameters_to_tuples(post_params, + collection_formats) + post_params.extend(self.files_parameters(files)) + if header_params['Content-Type'].startswith("multipart"): + post_params = self.parameters_to_multipart(post_params, + (dict) ) + + # body + if body: + body = self.sanitize_for_serialization(body) + + # auth setting + self.update_params_for_auth(header_params, query_params, + auth_settings, resource_path, method, body) + + # request url + if _host is None: + url = self.configuration.host + resource_path + else: + # use server/host defined in path or operation instead + url = _host + resource_path + + try: + # perform request and return response + response_data = self.request( + method, url, query_params=query_params, headers=header_params, + post_params=post_params, body=body, + _preload_content=_preload_content, + _request_timeout=_request_timeout) + except ApiException as e: + e.body = e.body.decode('utf-8') + raise e + + self.last_response = response_data + + return_data = response_data + + if not _preload_content: + return (return_data) + return return_data + + # deserialize response data + if response_type: + if response_type != (file_type,): + encoding = "utf-8" + content_type = response_data.getheader('content-type') + if content_type is not None: + match = re.search(r"charset=([a-zA-Z\-\d]+)[\s\;]?", content_type) + if match: + encoding = match.group(1) + response_data.data = response_data.data.decode(encoding) + + return_data = self.deserialize( + response_data, + response_type, + _check_type + ) + else: + return_data = None + + if _return_http_data_only: + return (return_data) + else: + return (return_data, response_data.status, + response_data.getheaders()) + + def parameters_to_multipart(self, params, collection_types): + """Get parameters as list of tuples, formatting as json if value is collection_types + + :param params: Parameters as list of two-tuples + :param dict collection_types: Parameter collection types + :return: Parameters as list of tuple or urllib3.fields.RequestField + """ + new_params = [] + if collection_types is None: + collection_types = (dict) + for k, v in params.items() if isinstance(params, dict) else params: # noqa: E501 + if isinstance(v, collection_types): # v is instance of collection_type, formatting as application/json + v = json.dumps(v, ensure_ascii=False).encode("utf-8") + field = RequestField(k, v) + field.make_multipart(content_type="application/json; charset=utf-8") + new_params.append(field) + else: + new_params.append((k, v)) + return new_params + + @classmethod + def sanitize_for_serialization(cls, obj): + """Prepares data for transmission before it is sent with the rest client + If obj is None, return None. + If obj is str, int, long, float, bool, return directly. + If obj is datetime.datetime, datetime.date + convert to string in iso8601 format. + If obj is list, sanitize each element in the list. + If obj is dict, return the dict. + If obj is OpenAPI model, return the properties dict. + If obj is io.IOBase, return the bytes + :param obj: The data to serialize. + :return: The serialized form of data. + """ + if isinstance(obj, (ModelNormal, ModelComposed)): + return { + key: cls.sanitize_for_serialization(val) for key, val in model_to_dict(obj, serialize=True).items() + } + elif isinstance(obj, io.IOBase): + return cls.get_file_data_and_close_file(obj) + elif isinstance(obj, (str, int, float, none_type, bool)): + return obj + elif isinstance(obj, (datetime, date)): + return obj.isoformat() + elif isinstance(obj, ModelSimple): + return cls.sanitize_for_serialization(obj.value) + elif isinstance(obj, (list, tuple)): + return [cls.sanitize_for_serialization(item) for item in obj] + if isinstance(obj, dict): + return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()} + raise ApiValueError('Unable to prepare type {} for serialization'.format(obj.__class__.__name__)) + + def deserialize(self, response, response_type, _check_type): + """Deserializes response into an object. + + :param response: RESTResponse object to be deserialized. + :param response_type: For the response, a tuple containing: + valid classes + a list containing valid classes (for list schemas) + a dict containing a tuple of valid classes as the value + Example values: + (str,) + (Pet,) + (float, none_type) + ([int, none_type],) + ({str: (bool, str, int, float, date, datetime, str, none_type)},) + :param _check_type: boolean, whether to check the types of the data + received from the server + :type _check_type: bool + + :return: deserialized object. + """ + # handle file downloading + # save response body into a tmp file and return the instance + if response_type == (file_type,): + content_disposition = response.getheader("Content-Disposition") + return deserialize_file(response.data, self.configuration, + content_disposition=content_disposition) + + # fetch data from response object + try: + received_data = json.loads(response.data) + except ValueError: + received_data = response.data + + # store our data under the key of 'received_data' so users have some + # context if they are deserializing a string and the data type is wrong + deserialized_data = validate_and_convert_types( + received_data, + response_type, + ['received_data'], + True, + _check_type, + configuration=self.configuration + ) + return deserialized_data + + def call_api( + self, + resource_path: str, + method: str, + path_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + query_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, + header_params: typing.Optional[typing.Dict[str, typing.Any]] = None, + body: typing.Optional[typing.Any] = None, + post_params: typing.Optional[typing.List[typing.Tuple[str, typing.Any]]] = None, + files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None, + response_type: typing.Optional[typing.Tuple[typing.Any]] = None, + auth_settings: typing.Optional[typing.List[str]] = None, + async_req: typing.Optional[bool] = None, + _return_http_data_only: typing.Optional[bool] = None, + collection_formats: typing.Optional[typing.Dict[str, str]] = None, + _preload_content: bool = True, + _request_timeout: typing.Optional[typing.Union[int, float, typing.Tuple]] = None, + _host: typing.Optional[str] = None, + _check_type: typing.Optional[bool] = None + ): + """Makes the HTTP request (synchronous) and returns deserialized data. + + To make an async_req request, set the async_req parameter. + + :param resource_path: Path to method endpoint. + :param method: Method to call. + :param path_params: Path parameters in the url. + :param query_params: Query parameters in the url. + :param header_params: Header parameters to be + placed in the request header. + :param body: Request body. + :param post_params dict: Request post form parameters, + for `application/x-www-form-urlencoded`, `multipart/form-data`. + :param auth_settings list: Auth Settings names for the request. + :param response_type: For the response, a tuple containing: + valid classes + a list containing valid classes (for list schemas) + a dict containing a tuple of valid classes as the value + Example values: + (str,) + (Pet,) + (float, none_type) + ([int, none_type],) + ({str: (bool, str, int, float, date, datetime, str, none_type)},) + :param files: key -> field name, value -> a list of open file + objects for `multipart/form-data`. + :type files: dict + :param async_req bool: execute request asynchronously + :type async_req: bool, optional + :param _return_http_data_only: response data without head status code + and headers + :type _return_http_data_only: bool, optional + :param collection_formats: dict of collection formats for path, query, + header, and post parameters. + :type collection_formats: dict, optional + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :type _preload_content: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _check_type: boolean describing if the data back from the server + should have its type checked. + :type _check_type: bool, optional + :return: + If async_req parameter is True, + the request will be called asynchronously. + The method will return the request thread. + If parameter async_req is False or missing, + then the method will return the response directly. + """ + if not async_req: + return self.__call_api(resource_path, method, + path_params, query_params, header_params, + body, post_params, files, + response_type, auth_settings, + _return_http_data_only, collection_formats, + _preload_content, _request_timeout, _host, + _check_type) + + return self.pool.apply_async(self.__call_api, (resource_path, + method, path_params, + query_params, + header_params, body, + post_params, files, + response_type, + auth_settings, + _return_http_data_only, + collection_formats, + _preload_content, + _request_timeout, + _host, _check_type)) + + def request(self, method, url, query_params=None, headers=None, + post_params=None, body=None, _preload_content=True, + _request_timeout=None): + """Makes the HTTP request using RESTClient.""" + if method == "GET": + return self.rest_client.GET(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "HEAD": + return self.rest_client.HEAD(url, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + headers=headers) + elif method == "OPTIONS": + return self.rest_client.OPTIONS(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "POST": + return self.rest_client.POST(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PUT": + return self.rest_client.PUT(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "PATCH": + return self.rest_client.PATCH(url, + query_params=query_params, + headers=headers, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + elif method == "DELETE": + return self.rest_client.DELETE(url, + query_params=query_params, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + else: + raise ApiValueError( + "http method must be `GET`, `HEAD`, `OPTIONS`," + " `POST`, `PATCH`, `PUT` or `DELETE`." + ) + + def parameters_to_tuples(self, params, collection_formats): + """Get parameters as list of tuples, formatting collections. + + :param params: Parameters as dict or list of two-tuples + :param dict collection_formats: Parameter collection formats + :return: Parameters as list of tuples, collections formatted + """ + new_params = [] + if collection_formats is None: + collection_formats = {} + for k, v in params.items() if isinstance(params, dict) else params: # noqa: E501 + if k in collection_formats: + collection_format = collection_formats[k] + if collection_format == 'multi': + new_params.extend((k, value) for value in v) + else: + if collection_format == 'ssv': + delimiter = ' ' + elif collection_format == 'tsv': + delimiter = '\t' + elif collection_format == 'pipes': + delimiter = '|' + else: # csv is the default + delimiter = ',' + new_params.append( + (k, delimiter.join(str(value) for value in v))) + else: + new_params.append((k, v)) + return new_params + + @staticmethod + def get_file_data_and_close_file(file_instance: io.IOBase) -> bytes: + file_data = file_instance.read() + file_instance.close() + return file_data + + def files_parameters(self, files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None): + """Builds form parameters. + + :param files: None or a dict with key=param_name and + value is a list of open file objects + :return: List of tuples of form parameters with file data + """ + if files is None: + return [] + + params = [] + for param_name, file_instances in files.items(): + if file_instances is None: + # if the file field is nullable, skip None values + continue + for file_instance in file_instances: + if file_instance is None: + # if the file field is nullable, skip None values + continue + if file_instance.closed is True: + raise ApiValueError( + "Cannot read a closed file. The passed in file_type " + "for %s must be open." % param_name + ) + filename = os.path.basename(file_instance.name) + filedata = self.get_file_data_and_close_file(file_instance) + mimetype = (mimetypes.guess_type(filename)[0] or + 'application/octet-stream') + params.append( + tuple([param_name, tuple([filename, filedata, mimetype])])) + + return params + + def select_header_accept(self, accepts): + """Returns `Accept` based on an array of accepts provided. + + :param accepts: List of headers. + :return: Accept (e.g. application/json). + """ + if not accepts: + return + + accepts = [x.lower() for x in accepts] + + if 'application/json' in accepts: + return 'application/json' + else: + return ', '.join(accepts) + + def select_header_content_type(self, content_types): + """Returns `Content-Type` based on an array of content_types provided. + + :param content_types: List of content-types. + :return: Content-Type (e.g. application/json). + """ + if not content_types: + return 'application/json' + + content_types = [x.lower() for x in content_types] + + if 'application/json' in content_types or '*/*' in content_types: + return 'application/json' + else: + return content_types[0] + + def update_params_for_auth(self, headers, querys, auth_settings, + resource_path, method, body): + """Updates header and query params based on authentication setting. + + :param headers: Header parameters dict to be updated. + :param querys: Query parameters tuple list to be updated. + :param auth_settings: Authentication setting identifiers list. + :param resource_path: A string representation of the HTTP request resource path. + :param method: A string representation of the HTTP request method. + :param body: A object representing the body of the HTTP request. + The object type is the return value of _encoder.default(). + """ + if not auth_settings: + return + + for auth in auth_settings: + auth_setting = self.configuration.auth_settings().get(auth) + if auth_setting: + if auth_setting['in'] == 'cookie': + headers['Cookie'] = auth_setting['value'] + elif auth_setting['in'] == 'header': + if auth_setting['type'] != 'http-signature': + headers[auth_setting['key']] = auth_setting['value'] + elif auth_setting['in'] == 'query': + querys.append((auth_setting['key'], auth_setting['value'])) + else: + raise ApiValueError( + 'Authentication token must be in `query` or `header`' + ) + + +class Endpoint(object): + def __init__(self, settings=None, params_map=None, root_map=None, + headers_map=None, api_client=None, callable=None): + """Creates an endpoint + + Args: + settings (dict): see below key value pairs + 'response_type' (tuple/None): response type + 'auth' (list): a list of auth type keys + 'endpoint_path' (str): the endpoint path + 'operation_id' (str): endpoint string identifier + 'http_method' (str): POST/PUT/PATCH/GET etc + 'servers' (list): list of str servers that this endpoint is at + params_map (dict): see below key value pairs + 'all' (list): list of str endpoint parameter names + 'required' (list): list of required parameter names + 'nullable' (list): list of nullable parameter names + 'enum' (list): list of parameters with enum values + 'validation' (list): list of parameters with validations + root_map + 'validations' (dict): the dict mapping endpoint parameter tuple + paths to their validation dictionaries + 'allowed_values' (dict): the dict mapping endpoint parameter + tuple paths to their allowed_values (enum) dictionaries + 'openapi_types' (dict): param_name to openapi type + 'attribute_map' (dict): param_name to camelCase name + 'location_map' (dict): param_name to 'body', 'file', 'form', + 'header', 'path', 'query' + collection_format_map (dict): param_name to `csv` etc. + headers_map (dict): see below key value pairs + 'accept' (list): list of Accept header strings + 'content_type' (list): list of Content-Type header strings + api_client (ApiClient) api client instance + callable (function): the function which is invoked when the + Endpoint is called + """ + self.settings = settings + self.params_map = params_map + self.params_map['all'].extend([ + 'async_req', + '_host_index', + '_preload_content', + '_request_timeout', + '_return_http_data_only', + '_check_input_type', + '_check_return_type' + ]) + self.params_map['nullable'].extend(['_request_timeout']) + self.validations = root_map['validations'] + self.allowed_values = root_map['allowed_values'] + self.openapi_types = root_map['openapi_types'] + extra_types = { + 'async_req': (bool,), + '_host_index': (none_type, int), + '_preload_content': (bool,), + '_request_timeout': (none_type, float, (float,), [float], int, (int,), [int]), + '_return_http_data_only': (bool,), + '_check_input_type': (bool,), + '_check_return_type': (bool,) + } + self.openapi_types.update(extra_types) + self.attribute_map = root_map['attribute_map'] + self.location_map = root_map['location_map'] + self.collection_format_map = root_map['collection_format_map'] + self.headers_map = headers_map + self.api_client = api_client + self.callable = callable + + def __validate_inputs(self, kwargs): + for param in self.params_map['enum']: + if param in kwargs: + check_allowed_values( + self.allowed_values, + (param,), + kwargs[param] + ) + + for param in self.params_map['validation']: + if param in kwargs: + check_validations( + self.validations, + (param,), + kwargs[param], + configuration=self.api_client.configuration + ) + + if kwargs['_check_input_type'] is False: + return + + for key, value in kwargs.items(): + fixed_val = validate_and_convert_types( + value, + self.openapi_types[key], + [key], + False, + kwargs['_check_input_type'], + configuration=self.api_client.configuration + ) + kwargs[key] = fixed_val + + def __gather_params(self, kwargs): + params = { + 'body': None, + 'collection_format': {}, + 'file': {}, + 'form': [], + 'header': {}, + 'path': {}, + 'query': [] + } + + for param_name, param_value in kwargs.items(): + param_location = self.location_map.get(param_name) + if param_location is None: + continue + if param_location: + if param_location == 'body': + params['body'] = param_value + continue + base_name = self.attribute_map[param_name] + if (param_location == 'form' and + self.openapi_types[param_name] == (file_type,)): + params['file'][param_name] = [param_value] + elif (param_location == 'form' and + self.openapi_types[param_name] == ([file_type],)): + # param_value is already a list + params['file'][param_name] = param_value + elif param_location in {'form', 'query'}: + param_value_full = (base_name, param_value) + params[param_location].append(param_value_full) + if param_location not in {'form', 'query'}: + params[param_location][base_name] = param_value + collection_format = self.collection_format_map.get(param_name) + if collection_format: + params['collection_format'][base_name] = collection_format + + return params + + def __call__(self, *args, **kwargs): + """ This method is invoked when endpoints are called + Example: + + api_instance = DistancesApi() + api_instance.dmx_from_mongodb_v1_distance_calculations_post # this is an instance of the class Endpoint + api_instance.dmx_from_mongodb_v1_distance_calculations_post() # this invokes api_instance.dmx_from_mongodb_v1_distance_calculations_post.__call__() + which then invokes the callable functions stored in that endpoint at + api_instance.dmx_from_mongodb_v1_distance_calculations_post.callable or self.callable in this class + + """ + return self.callable(self, *args, **kwargs) + + def call_with_http_info(self, **kwargs): + + try: + index = self.api_client.configuration.server_operation_index.get( + self.settings['operation_id'], self.api_client.configuration.server_index + ) if kwargs['_host_index'] is None else kwargs['_host_index'] + server_variables = self.api_client.configuration.server_operation_variables.get( + self.settings['operation_id'], self.api_client.configuration.server_variables + ) + _host = self.api_client.configuration.get_host_from_settings( + index, variables=server_variables, servers=self.settings['servers'] + ) + except IndexError: + if self.settings['servers']: + raise ApiValueError( + "Invalid host index. Must be 0 <= index < %s" % + len(self.settings['servers']) + ) + _host = None + + for key, value in kwargs.items(): + if key not in self.params_map['all']: + raise ApiTypeError( + "Got an unexpected parameter '%s'" + " to method `%s`" % + (key, self.settings['operation_id']) + ) + # only throw this nullable ApiValueError if _check_input_type + # is False, if _check_input_type==True we catch this case + # in self.__validate_inputs + if (key not in self.params_map['nullable'] and value is None + and kwargs['_check_input_type'] is False): + raise ApiValueError( + "Value may not be None for non-nullable parameter `%s`" + " when calling `%s`" % + (key, self.settings['operation_id']) + ) + + for key in self.params_map['required']: + if key not in kwargs.keys(): + raise ApiValueError( + "Missing the required parameter `%s` when calling " + "`%s`" % (key, self.settings['operation_id']) + ) + + self.__validate_inputs(kwargs) + + params = self.__gather_params(kwargs) + + accept_headers_list = self.headers_map['accept'] + if accept_headers_list: + params['header']['Accept'] = self.api_client.select_header_accept( + accept_headers_list) + + content_type_headers_list = self.headers_map['content_type'] + if content_type_headers_list: + header_list = self.api_client.select_header_content_type( + content_type_headers_list) + params['header']['Content-Type'] = header_list + + return self.api_client.call_api( + self.settings['endpoint_path'], self.settings['http_method'], + params['path'], + params['query'], + params['header'], + body=params['body'], + post_params=params['form'], + files=params['file'], + response_type=self.settings['response_type'], + auth_settings=self.settings['auth'], + async_req=kwargs['async_req'], + _check_type=kwargs['_check_return_type'], + _return_http_data_only=kwargs['_return_http_data_only'], + _preload_content=kwargs['_preload_content'], + _request_timeout=kwargs['_request_timeout'], + _host=_host, + collection_formats=params['collection_format']) diff --git a/web/src/services/bio_api/openapi/apis/__init__.py b/web/src/services/bio_api/openapi/apis/__init__.py new file mode 100644 index 00000000..23487105 --- /dev/null +++ b/web/src/services/bio_api/openapi/apis/__init__.py @@ -0,0 +1,19 @@ + +# flake8: noqa + +# Import all APIs into this package. +# If you have many APIs here with many many models used in each API this may +# raise a `RecursionError`. +# In order to avoid this, import only the API that you directly need like: +# +# from .api.distances_api import DistancesApi +# +# or import this package, but before doing it, use: +# +# import sys +# sys.setrecursionlimit(n) + +# Import APIs into API package: +from web.src.services.bio_api.openapi.api.distances_api import DistancesApi +from web.src.services.bio_api.openapi.api.nearest_neighbors_api import NearestNeighborsApi +from web.src.services.bio_api.openapi.api.trees_api import TreesApi diff --git a/web/src/services/bio_api/openapi/configuration.py b/web/src/services/bio_api/openapi/configuration.py new file mode 100644 index 00000000..a8f90aa5 --- /dev/null +++ b/web/src/services/bio_api/openapi/configuration.py @@ -0,0 +1,443 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import copy +import logging +import multiprocessing +import sys +import urllib3 + +from http import client as http_client +from web.src.services.bio_api.openapi.exceptions import ApiValueError + + +JSON_SCHEMA_VALIDATION_KEYWORDS = { + 'multipleOf', 'maximum', 'exclusiveMaximum', + 'minimum', 'exclusiveMinimum', 'maxLength', + 'minLength', 'pattern', 'maxItems', 'minItems' +} + +class Configuration(object): + """NOTE: This class is auto generated by OpenAPI Generator + + Ref: https://openapi-generator.tech + Do not edit the class manually. + + :param host: Base url + :param api_key: Dict to store API key(s). + Each entry in the dict specifies an API key. + The dict key is the name of the security scheme in the OAS specification. + The dict value is the API key secret. + :param api_key_prefix: Dict to store API prefix (e.g. Bearer) + The dict key is the name of the security scheme in the OAS specification. + The dict value is an API key prefix when generating the auth data. + :param username: Username for HTTP basic authentication + :param password: Password for HTTP basic authentication + :param discard_unknown_keys: Boolean value indicating whether to discard + unknown properties. A server may send a response that includes additional + properties that are not known by the client in the following scenarios: + 1. The OpenAPI document is incomplete, i.e. it does not match the server + implementation. + 2. The client was generated using an older version of the OpenAPI document + and the server has been upgraded since then. + If a schema in the OpenAPI document defines the additionalProperties attribute, + then all undeclared properties received by the server are injected into the + additional properties map. In that case, there are undeclared properties, and + nothing to discard. + :param disabled_client_side_validations (string): Comma-separated list of + JSON schema validation keywords to disable JSON schema structural validation + rules. The following keywords may be specified: multipleOf, maximum, + exclusiveMaximum, minimum, exclusiveMinimum, maxLength, minLength, pattern, + maxItems, minItems. + By default, the validation is performed for data generated locally by the client + and data received from the server, independent of any validation performed by + the server side. If the input data does not satisfy the JSON schema validation + rules specified in the OpenAPI document, an exception is raised. + If disabled_client_side_validations is set, structural validation is + disabled. This can be useful to troubleshoot data validation problem, such as + when the OpenAPI document validation rules do not match the actual API data + received by the server. + :param server_index: Index to servers configuration. + :param server_variables: Mapping with string values to replace variables in + templated server configuration. The validation of enums is performed for + variables with defined enum values before. + :param server_operation_index: Mapping from operation ID to an index to server + configuration. + :param server_operation_variables: Mapping from operation ID to a mapping with + string values to replace variables in templated server configuration. + The validation of enums is performed for variables with defined enum values before. + :param ssl_ca_cert: str - the path to a file of concatenated CA certificates + in PEM format + + """ + + _default = None + + def __init__(self, host=None, + api_key=None, api_key_prefix=None, + access_token=None, + username=None, password=None, + discard_unknown_keys=False, + disabled_client_side_validations="", + server_index=None, server_variables=None, + server_operation_index=None, server_operation_variables=None, + ssl_ca_cert=None, + ): + """Constructor + """ + self._base_path = "http://localhost:8000" if host is None else host + """Default Base url + """ + self.server_index = 0 if server_index is None and host is None else server_index + self.server_operation_index = server_operation_index or {} + """Default server index + """ + self.server_variables = server_variables or {} + self.server_operation_variables = server_operation_variables or {} + """Default server variables + """ + self.temp_folder_path = None + """Temp file folder for downloading files + """ + # Authentication Settings + self.access_token = access_token + self.api_key = {} + if api_key: + self.api_key = api_key + """dict to store API key(s) + """ + self.api_key_prefix = {} + if api_key_prefix: + self.api_key_prefix = api_key_prefix + """dict to store API prefix (e.g. Bearer) + """ + self.refresh_api_key_hook = None + """function hook to refresh API key if expired + """ + self.username = username + """Username for HTTP basic authentication + """ + self.password = password + """Password for HTTP basic authentication + """ + self.discard_unknown_keys = discard_unknown_keys + self.disabled_client_side_validations = disabled_client_side_validations + self.logger = {} + """Logging Settings + """ + self.logger["package_logger"] = logging.getLogger("web.src.services.bio_api.openapi") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + self.logger_format = '%(asctime)s %(levelname)s %(message)s' + """Log format + """ + self.logger_stream_handler = None + """Log stream handler + """ + self.logger_file_handler = None + """Log file handler + """ + self.logger_file = None + """Debug file location + """ + self.debug = False + """Debug switch + """ + + self.verify_ssl = True + """SSL/TLS verification + Set this to false to skip verifying SSL certificate when calling API + from https server. + """ + self.ssl_ca_cert = ssl_ca_cert + """Set this to customize the certificate file to verify the peer. + """ + self.cert_file = None + """client certificate file + """ + self.key_file = None + """client key file + """ + self.assert_hostname = None + """Set this to True/False to enable/disable SSL hostname verification. + """ + + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + """urllib3 connection pool's maximum number of connections saved + per pool. urllib3 uses 1 connection as default value, but this is + not the best value when you are making a lot of possibly parallel + requests to the same host, which is often the case here. + cpu_count * 5 is used as default value to increase performance. + """ + + self.proxy = None + """Proxy URL + """ + self.proxy_headers = None + """Proxy headers + """ + self.safe_chars_for_path_param = '' + """Safe chars for path_param + """ + self.retries = None + """Adding retries to override urllib3 default value 3 + """ + # Enable client side validation + self.client_side_validation = True + + # Options to pass down to the underlying urllib3 socket + self.socket_options = None + + def __deepcopy__(self, memo): + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k not in ('logger', 'logger_file_handler'): + setattr(result, k, copy.deepcopy(v, memo)) + # shallow copy of loggers + result.logger = copy.copy(self.logger) + # use setters to configure loggers + result.logger_file = self.logger_file + result.debug = self.debug + return result + + def __setattr__(self, name, value): + object.__setattr__(self, name, value) + if name == 'disabled_client_side_validations': + s = set(filter(None, value.split(','))) + for v in s: + if v not in JSON_SCHEMA_VALIDATION_KEYWORDS: + raise ApiValueError( + "Invalid keyword: '{0}''".format(v)) + self._disabled_client_side_validations = s + + @classmethod + def set_default(cls, default): + """Set default instance of configuration. + + It stores default configuration, which can be + returned by get_default_copy method. + + :param default: object of Configuration + """ + cls._default = copy.deepcopy(default) + + @classmethod + def get_default_copy(cls): + """Return new instance of configuration. + + This method returns newly created, based on default constructor, + object of Configuration class or returns a copy of default + configuration passed by the set_default method. + + :return: The configuration object. + """ + if cls._default is not None: + return copy.deepcopy(cls._default) + return Configuration() + + @property + def logger_file(self): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in self.logger.items(): + logger.addHandler(self.logger_file_handler) + + @property + def debug(self): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in self.logger.items(): + logger.setLevel(logging.DEBUG) + # turn on http_client debug + http_client.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in self.logger.items(): + logger.setLevel(logging.WARNING) + # turn off http_client debug + http_client.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier, alias=None): + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :param alias: The alternative identifier of apiKey. + :return: The token for api key authentication. + """ + if self.refresh_api_key_hook is not None: + self.refresh_api_key_hook(self) + key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) + if key: + prefix = self.api_key_prefix.get(identifier) + if prefix: + return "%s %s" % (prefix, key) + else: + return key + + def get_basic_auth_token(self): + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + username = "" + if self.username is not None: + username = self.username + password = "" + if self.password is not None: + password = self.password + return urllib3.util.make_headers( + basic_auth=username + ':' + password + ).get('authorization') + + def auth_settings(self): + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + auth = {} + return auth + + def to_debug_report(self): + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + "OS: {env}\n"\ + "Python Version: {pyversion}\n"\ + "Version of the API: 0.2.0\n"\ + "SDK Package Version: 1.0.0".\ + format(env=sys.platform, pyversion=sys.version) + + def get_host_settings(self): + """Gets an array of host settings + + :return: An array of host settings + """ + return [ + { + 'url': "http://localhost:8000", + 'description': "No description provided", + } + ] + + def get_host_from_settings(self, index, variables=None, servers=None): + """Gets host URL based on the index and variables + :param index: array index of the host settings + :param variables: hash of variable and the corresponding value + :param servers: an array of host settings or None + :return: URL based on host settings + """ + if index is None: + return self._base_path + + variables = {} if variables is None else variables + servers = self.get_host_settings() if servers is None else servers + + try: + server = servers[index] + except IndexError: + raise ValueError( + "Invalid index {0} when selecting the host settings. " + "Must be less than {1}".format(index, len(servers))) + + url = server['url'] + + # go through variables and replace placeholders + for variable_name, variable in server.get('variables', {}).items(): + used_value = variables.get( + variable_name, variable['default_value']) + + if 'enum_values' in variable \ + and used_value not in variable['enum_values']: + raise ValueError( + "The variable `{0}` in the host URL has invalid value " + "{1}. Must be {2}.".format( + variable_name, variables[variable_name], + variable['enum_values'])) + + url = url.replace("{" + variable_name + "}", used_value) + + return url + + @property + def host(self): + """Return generated host.""" + return self.get_host_from_settings(self.server_index, variables=self.server_variables) + + @host.setter + def host(self, value): + """Fix base path.""" + self._base_path = value + self.server_index = None diff --git a/web/src/services/bio_api/openapi/exceptions.py b/web/src/services/bio_api/openapi/exceptions.py new file mode 100644 index 00000000..ab6afaf1 --- /dev/null +++ b/web/src/services/bio_api/openapi/exceptions.py @@ -0,0 +1,159 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, + key_type=None): + """ Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiTypeError, self).__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiValueError, self).__init__(full_msg) + + +class ApiAttributeError(OpenApiException, AttributeError): + def __init__(self, msg, path_to_item=None): + """ + Raised when an attribute reference or assignment fails. + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiAttributeError, self).__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiKeyError, self).__init__(full_msg) + + +class ApiException(OpenApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\n"\ + "Reason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format( + self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message + + +class NotFoundException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(NotFoundException, self).__init__(status, reason, http_resp) + + +class UnauthorizedException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(UnauthorizedException, self).__init__(status, reason, http_resp) + + +class ForbiddenException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(ForbiddenException, self).__init__(status, reason, http_resp) + + +class ServiceException(ApiException): + + def __init__(self, status=None, reason=None, http_resp=None): + super(ServiceException, self).__init__(status, reason, http_resp) + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, int): + result += "[{0}]".format(pth) + else: + result += "['{0}']".format(pth) + return result diff --git a/web/src/services/bio_api/openapi/model/__init__.py b/web/src/services/bio_api/openapi/model/__init__.py new file mode 100644 index 00000000..cfe32b78 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/__init__.py @@ -0,0 +1,5 @@ +# we can not import model classes here because that would create a circular +# reference which would not work in python2 +# do not import all models into this module because that uses a lot of memory and stack frames +# if you need the ability to import all models from one package, import them with +# from {{packageName}.models import ModelA, ModelB diff --git a/web/src/services/bio_api/openapi/model/common_post_response.py b/web/src/services/bio_api/openapi/model/common_post_response.py new file mode 100644 index 00000000..d1ed9d84 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/common_post_response.py @@ -0,0 +1,279 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + +def lazy_import(): + from web.src.services.bio_api.openapi.model.status import Status + globals()['Status'] = Status + + +class CommonPOSTResponse(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'job_id': (str,), # noqa: E501 + 'created_at': (str,), # noqa: E501 + 'status': (Status,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'job_id': 'job_id', # noqa: E501 + 'created_at': 'created_at', # noqa: E501 + 'status': 'status', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, job_id, created_at, status, *args, **kwargs): # noqa: E501 + """CommonPOSTResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, job_id, created_at, status, *args, **kwargs): # noqa: E501 + """CommonPOSTResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py b/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py new file mode 100644 index 00000000..10094a5a --- /dev/null +++ b/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py @@ -0,0 +1,317 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + +def lazy_import(): + from web.src.services.bio_api.openapi.model.distance_matrix_result import DistanceMatrixResult + from web.src.services.bio_api.openapi.model.status import Status + globals()['DistanceMatrixResult'] = DistanceMatrixResult + globals()['Status'] = Status + + +class DistanceMatrixGETResponse(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'job_id': (str,), # noqa: E501 + 'created_at': (str,), # noqa: E501 + 'status': (Status,), # noqa: E501 + 'finished_at': (dict,), # noqa: E501 + 'seq_collection': (str,), # noqa: E501 + 'seqid_field_path': (str,), # noqa: E501 + 'profile_field_path': (str,), # noqa: E501 + 'seq_mongo_ids': ([bool, date, datetime, dict, float, int, list, str, none_type],), # noqa: E501 + 'result': (DistanceMatrixResult,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'job_id': 'job_id', # noqa: E501 + 'created_at': 'created_at', # noqa: E501 + 'status': 'status', # noqa: E501 + 'finished_at': 'finished_at', # noqa: E501 + 'seq_collection': 'seq_collection', # noqa: E501 + 'seqid_field_path': 'seqid_field_path', # noqa: E501 + 'profile_field_path': 'profile_field_path', # noqa: E501 + 'seq_mongo_ids': 'seq_mongo_ids', # noqa: E501 + 'result': 'result', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collection, seqid_field_path, profile_field_path, seq_mongo_ids, result, *args, **kwargs): # noqa: E501 + """DistanceMatrixGETResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + finished_at (dict): + seq_collection (str): + seqid_field_path (str): + profile_field_path (str): + seq_mongo_ids ([bool, date, datetime, dict, float, int, list, str, none_type]): + result (DistanceMatrixResult): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + self.finished_at = finished_at + self.seq_collection = seq_collection + self.seqid_field_path = seqid_field_path + self.profile_field_path = profile_field_path + self.seq_mongo_ids = seq_mongo_ids + self.result = result + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, job_id, created_at, status, finished_at, seq_collection, seqid_field_path, profile_field_path, seq_mongo_ids, result, *args, **kwargs): # noqa: E501 + """DistanceMatrixGETResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + finished_at (dict): + seq_collection (str): + seqid_field_path (str): + profile_field_path (str): + seq_mongo_ids ([bool, date, datetime, dict, float, int, list, str, none_type]): + result (DistanceMatrixResult): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + self.finished_at = finished_at + self.seq_collection = seq_collection + self.seqid_field_path = seqid_field_path + self.profile_field_path = profile_field_path + self.seq_mongo_ids = seq_mongo_ids + self.result = result + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/distance_matrix_request.py b/web/src/services/bio_api/openapi/model/distance_matrix_request.py new file mode 100644 index 00000000..8ed97b5c --- /dev/null +++ b/web/src/services/bio_api/openapi/model/distance_matrix_request.py @@ -0,0 +1,279 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class DistanceMatrixRequest(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'seq_collection': (str,), # noqa: E501 + 'seqid_field_path': (str,), # noqa: E501 + 'profile_field_path': (str,), # noqa: E501 + 'seq_mongo_ids': ([bool, date, datetime, dict, float, int, list, str, none_type],), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'seq_collection': 'seq_collection', # noqa: E501 + 'seqid_field_path': 'seqid_field_path', # noqa: E501 + 'profile_field_path': 'profile_field_path', # noqa: E501 + 'seq_mongo_ids': 'seq_mongo_ids', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, seq_collection, seqid_field_path, profile_field_path, seq_mongo_ids, *args, **kwargs): # noqa: E501 + """DistanceMatrixRequest - a model defined in OpenAPI + + Args: + seq_collection (str): + seqid_field_path (str): + profile_field_path (str): + seq_mongo_ids ([bool, date, datetime, dict, float, int, list, str, none_type]): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.seq_collection = seq_collection + self.seqid_field_path = seqid_field_path + self.profile_field_path = profile_field_path + self.seq_mongo_ids = seq_mongo_ids + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, seq_collection, seqid_field_path, profile_field_path, seq_mongo_ids, *args, **kwargs): # noqa: E501 + """DistanceMatrixRequest - a model defined in OpenAPI + + Args: + seq_collection (str): + seqid_field_path (str): + profile_field_path (str): + seq_mongo_ids ([bool, date, datetime, dict, float, int, list, str, none_type]): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.seq_collection = seq_collection + self.seqid_field_path = seqid_field_path + self.profile_field_path = profile_field_path + self.seq_mongo_ids = seq_mongo_ids + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/distance_matrix_result.py b/web/src/services/bio_api/openapi/model/distance_matrix_result.py new file mode 100644 index 00000000..9c5c37f5 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/distance_matrix_result.py @@ -0,0 +1,265 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class DistanceMatrixResult(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'seq_to_mongo': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), # noqa: E501 + 'distances': (dict,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'seq_to_mongo': 'seq_to_mongo', # noqa: E501 + 'distances': 'distances', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, seq_to_mongo, *args, **kwargs): # noqa: E501 + """DistanceMatrixResult - a model defined in OpenAPI + + Args: + seq_to_mongo ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + distances (dict): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.seq_to_mongo = seq_to_mongo + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, seq_to_mongo, *args, **kwargs): # noqa: E501 + """DistanceMatrixResult - a model defined in OpenAPI + + Args: + seq_to_mongo ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + distances (dict): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.seq_to_mongo = seq_to_mongo + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py b/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py new file mode 100644 index 00000000..bbfedee0 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py @@ -0,0 +1,312 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + +def lazy_import(): + from web.src.services.bio_api.openapi.model.status import Status + globals()['Status'] = Status + + +class HCTreeCalcGETResponse(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('method',): { + 'SINGLE': "single", + 'COMPLETE': "complete", + 'AVERAGE': "average", + 'WEIGHTED': "weighted", + 'CENTROID': "centroid", + 'MEDIAN': "median", + 'WARD': "ward", + }, + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'job_id': (str,), # noqa: E501 + 'created_at': (str,), # noqa: E501 + 'status': (Status,), # noqa: E501 + 'finished_at': (dict,), # noqa: E501 + 'dmx_job': (str,), # noqa: E501 + 'method': (str,), # noqa: E501 + 'result': (dict,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'job_id': 'job_id', # noqa: E501 + 'created_at': 'created_at', # noqa: E501 + 'status': 'status', # noqa: E501 + 'finished_at': 'finished_at', # noqa: E501 + 'dmx_job': 'dmx_job', # noqa: E501 + 'method': 'method', # noqa: E501 + 'result': 'result', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, job_id, created_at, status, finished_at, dmx_job, method, result, *args, **kwargs): # noqa: E501 + """HCTreeCalcGETResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + finished_at (dict): + dmx_job (str): + method (str): + result (dict): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + self.finished_at = finished_at + self.dmx_job = dmx_job + self.method = method + self.result = result + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, job_id, created_at, status, finished_at, dmx_job, method, result, *args, **kwargs): # noqa: E501 + """HCTreeCalcGETResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + finished_at (dict): + dmx_job (str): + method (str): + result (dict): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + self.finished_at = finished_at + self.dmx_job = dmx_job + self.method = method + self.result = result + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py b/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py new file mode 100644 index 00000000..cbea077d --- /dev/null +++ b/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py @@ -0,0 +1,276 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class HCTreeCalcRequest(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('method',): { + 'SINGLE': "single", + 'COMPLETE': "complete", + 'AVERAGE': "average", + 'WEIGHTED': "weighted", + 'CENTROID': "centroid", + 'MEDIAN': "median", + 'WARD': "ward", + }, + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'dmx_job': (str,), # noqa: E501 + 'method': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'dmx_job': 'dmx_job', # noqa: E501 + 'method': 'method', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, dmx_job, method, *args, **kwargs): # noqa: E501 + """HCTreeCalcRequest - a model defined in OpenAPI + + Args: + dmx_job (str): + method (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.dmx_job = dmx_job + self.method = method + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, dmx_job, method, *args, **kwargs): # noqa: E501 + """HCTreeCalcRequest - a model defined in OpenAPI + + Args: + dmx_job (str): + method (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.dmx_job = dmx_job + self.method = method + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/http_validation_error.py b/web/src/services/bio_api/openapi/model/http_validation_error.py new file mode 100644 index 00000000..3a70e4ac --- /dev/null +++ b/web/src/services/bio_api/openapi/model/http_validation_error.py @@ -0,0 +1,261 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + +def lazy_import(): + from web.src.services.bio_api.openapi.model.validation_error import ValidationError + globals()['ValidationError'] = ValidationError + + +class HTTPValidationError(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'detail': ([ValidationError],), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'detail': 'detail', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, *args, **kwargs): # noqa: E501 + """HTTPValidationError - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + detail ([ValidationError]): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """HTTPValidationError - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + detail ([ValidationError]): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/message.py b/web/src/services/bio_api/openapi/model/message.py new file mode 100644 index 00000000..15ead9e4 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/message.py @@ -0,0 +1,261 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class Message(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'detail': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'detail': 'detail', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, detail, *args, **kwargs): # noqa: E501 + """Message - a model defined in OpenAPI + + Args: + detail (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.detail = detail + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, detail, *args, **kwargs): # noqa: E501 + """Message - a model defined in OpenAPI + + Args: + detail (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.detail = detail + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py b/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py new file mode 100644 index 00000000..af1766d4 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py @@ -0,0 +1,325 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + +def lazy_import(): + from web.src.services.bio_api.openapi.model.status import Status + globals()['Status'] = Status + + +class NearestNeighborsGETResponse(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + 'job_id': (str,), # noqa: E501 + 'created_at': (str,), # noqa: E501 + 'status': (Status,), # noqa: E501 + 'finished_at': (dict,), # noqa: E501 + 'seq_collection': (str,), # noqa: E501 + 'profile_field_path': (str,), # noqa: E501 + 'input_mongo_id': (str,), # noqa: E501 + 'cutoff': (int,), # noqa: E501 + 'unknowns_are_diffs': (bool,), # noqa: E501 + 'result': (dict,), # noqa: E501 + 'filtering': (dict,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'job_id': 'job_id', # noqa: E501 + 'created_at': 'created_at', # noqa: E501 + 'status': 'status', # noqa: E501 + 'finished_at': 'finished_at', # noqa: E501 + 'seq_collection': 'seq_collection', # noqa: E501 + 'profile_field_path': 'profile_field_path', # noqa: E501 + 'input_mongo_id': 'input_mongo_id', # noqa: E501 + 'cutoff': 'cutoff', # noqa: E501 + 'unknowns_are_diffs': 'unknowns_are_diffs', # noqa: E501 + 'result': 'result', # noqa: E501 + 'filtering': 'filtering', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, result, *args, **kwargs): # noqa: E501 + """NearestNeighborsGETResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + finished_at (dict): + seq_collection (str): + profile_field_path (str): + input_mongo_id (str): + cutoff (int): + unknowns_are_diffs (bool): + result (dict): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + filtering (dict): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + self.finished_at = finished_at + self.seq_collection = seq_collection + self.profile_field_path = profile_field_path + self.input_mongo_id = input_mongo_id + self.cutoff = cutoff + self.unknowns_are_diffs = unknowns_are_diffs + self.result = result + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, job_id, created_at, status, finished_at, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, result, *args, **kwargs): # noqa: E501 + """NearestNeighborsGETResponse - a model defined in OpenAPI + + Args: + job_id (str): + created_at (str): + status (Status): + finished_at (dict): + seq_collection (str): + profile_field_path (str): + input_mongo_id (str): + cutoff (int): + unknowns_are_diffs (bool): + result (dict): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + filtering (dict): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.job_id = job_id + self.created_at = created_at + self.status = status + self.finished_at = finished_at + self.seq_collection = seq_collection + self.profile_field_path = profile_field_path + self.input_mongo_id = input_mongo_id + self.cutoff = cutoff + self.unknowns_are_diffs = unknowns_are_diffs + self.result = result + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py b/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py new file mode 100644 index 00000000..d821a90d --- /dev/null +++ b/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py @@ -0,0 +1,289 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class NearestNeighborsRequest(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'seq_collection': (str,), # noqa: E501 + 'profile_field_path': (str,), # noqa: E501 + 'input_mongo_id': (str,), # noqa: E501 + 'cutoff': (int,), # noqa: E501 + 'unknowns_are_diffs': (bool,), # noqa: E501 + 'filtering': (dict,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'seq_collection': 'seq_collection', # noqa: E501 + 'profile_field_path': 'profile_field_path', # noqa: E501 + 'input_mongo_id': 'input_mongo_id', # noqa: E501 + 'cutoff': 'cutoff', # noqa: E501 + 'unknowns_are_diffs': 'unknowns_are_diffs', # noqa: E501 + 'filtering': 'filtering', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, *args, **kwargs): # noqa: E501 + """NearestNeighborsRequest - a model defined in OpenAPI + + Args: + seq_collection (str): + profile_field_path (str): + input_mongo_id (str): + cutoff (int): + unknowns_are_diffs (bool): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + filtering (dict): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.seq_collection = seq_collection + self.profile_field_path = profile_field_path + self.input_mongo_id = input_mongo_id + self.cutoff = cutoff + self.unknowns_are_diffs = unknowns_are_diffs + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, *args, **kwargs): # noqa: E501 + """NearestNeighborsRequest - a model defined in OpenAPI + + Args: + seq_collection (str): + profile_field_path (str): + input_mongo_id (str): + cutoff (int): + unknowns_are_diffs (bool): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + filtering (dict): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.seq_collection = seq_collection + self.profile_field_path = profile_field_path + self.input_mongo_id = input_mongo_id + self.cutoff = cutoff + self.unknowns_are_diffs = unknowns_are_diffs + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/neighbor.py b/web/src/services/bio_api/openapi/model/neighbor.py new file mode 100644 index 00000000..a8583ccd --- /dev/null +++ b/web/src/services/bio_api/openapi/model/neighbor.py @@ -0,0 +1,267 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class Neighbor(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'id': (str,), # noqa: E501 + 'diff_count': (int,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'id': 'id', # noqa: E501 + 'diff_count': 'diff_count', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, id, diff_count, *args, **kwargs): # noqa: E501 + """Neighbor - a model defined in OpenAPI + + Args: + id (str): + diff_count (int): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.id = id + self.diff_count = diff_count + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, id, diff_count, *args, **kwargs): # noqa: E501 + """Neighbor - a model defined in OpenAPI + + Args: + id (str): + diff_count (int): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.id = id + self.diff_count = diff_count + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model/status.py b/web/src/services/bio_api/openapi/model/status.py new file mode 100644 index 00000000..70965497 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/status.py @@ -0,0 +1,289 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class Status(ModelSimple): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + ('value',): { + 'INIT': "init", + 'COMPLETED': "completed", + 'ERROR': "error", + }, + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'value': (str,), + } + + @cached_property + def discriminator(): + return None + + + attribute_map = {} + + read_only_vars = set() + + _composed_schemas = None + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): + """Status - a model defined in OpenAPI + + Note that value can be passed either in args or in kwargs, but not in both. + + Args: + args[0] (str):, must be one of ["init", "completed", "error", ] # noqa: E501 + + Keyword Args: + value (str):, must be one of ["init", "completed", "error", ] # noqa: E501 + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + # required up here when default value is not given + _path_to_item = kwargs.pop('_path_to_item', ()) + + if 'value' in kwargs: + value = kwargs.pop('value') + elif args: + args = list(args) + value = args.pop(0) + else: + raise ApiTypeError( + "value is required, but not passed in args or kwargs and doesn't have default", + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + self.value = value + if kwargs: + raise ApiTypeError( + "Invalid named arguments=%s passed to %s. Remove those invalid named arguments." % ( + kwargs, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, *args, **kwargs): + """Status - a model defined in OpenAPI + + Note that value can be passed either in args or in kwargs, but not in both. + + Args: + args[0] (str):, must be one of ["init", "completed", "error", ] # noqa: E501 + + Keyword Args: + value (str):, must be one of ["init", "completed", "error", ] # noqa: E501 + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + # required up here when default value is not given + _path_to_item = kwargs.pop('_path_to_item', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if 'value' in kwargs: + value = kwargs.pop('value') + elif args: + args = list(args) + value = args.pop(0) + else: + raise ApiTypeError( + "value is required, but not passed in args or kwargs and doesn't have default", + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + self.value = value + if kwargs: + raise ApiTypeError( + "Invalid named arguments=%s passed to %s. Remove those invalid named arguments." % ( + kwargs, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + return self diff --git a/web/src/services/bio_api/openapi/model/validation_error.py b/web/src/services/bio_api/openapi/model/validation_error.py new file mode 100644 index 00000000..631aa915 --- /dev/null +++ b/web/src/services/bio_api/openapi/model/validation_error.py @@ -0,0 +1,273 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from web.src.services.bio_api.openapi.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) +from ..model_utils import OpenApiModel +from web.src.services.bio_api.openapi.exceptions import ApiAttributeError + + + +class ValidationError(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'loc': ([dict],), # noqa: E501 + 'msg': (str,), # noqa: E501 + 'type': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'loc': 'loc', # noqa: E501 + 'msg': 'msg', # noqa: E501 + 'type': 'type', # noqa: E501 + } + + read_only_vars = { + } + + _composed_schemas = {} + + @classmethod + @convert_js_args_to_python_args + def _from_openapi_data(cls, loc, msg, type, *args, **kwargs): # noqa: E501 + """ValidationError - a model defined in OpenAPI + + Args: + loc ([dict]): + msg (str): + type (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + self = super(OpenApiModel, cls).__new__(cls) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.loc = loc + self.msg = msg + self.type = type + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + return self + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, loc, msg, type, *args, **kwargs): # noqa: E501 + """ValidationError - a model defined in OpenAPI + + Args: + loc ([dict]): + msg (str): + type (str): + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.loc = loc + self.msg = msg + self.type = type + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) + if var_name in self.read_only_vars: + raise ApiAttributeError(f"`{var_name}` is a read-only attribute. Use `from_openapi_data` to instantiate " + f"class with read only attributes.") diff --git a/web/src/services/bio_api/openapi/model_utils.py b/web/src/services/bio_api/openapi/model_utils.py new file mode 100644 index 00000000..27c90d0d --- /dev/null +++ b/web/src/services/bio_api/openapi/model_utils.py @@ -0,0 +1,1992 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +from datetime import date, datetime # noqa: F401 +import inspect +import io +import os +import pprint +import re +import tempfile + +from dateutil.parser import parse + +from web.src.services.bio_api.openapi.exceptions import ( + ApiKeyError, + ApiAttributeError, + ApiTypeError, + ApiValueError, +) + +none_type = type(None) +file_type = io.IOBase + + +def convert_js_args_to_python_args(fn): + from functools import wraps + @wraps(fn) + def wrapped_init(_self, *args, **kwargs): + """ + An attribute named `self` received from the api will conflicts with the reserved `self` + parameter of a class method. During generation, `self` attributes are mapped + to `_self` in models. Here, we name `_self` instead of `self` to avoid conflicts. + """ + spec_property_naming = kwargs.get('_spec_property_naming', False) + if spec_property_naming: + kwargs = change_keys_js_to_python(kwargs, _self if isinstance(_self, type) else _self.__class__) + return fn(_self, *args, **kwargs) + return wrapped_init + + +class cached_property(object): + # this caches the result of the function call for fn with no inputs + # use this as a decorator on function methods that you want converted + # into cached properties + result_key = '_results' + + def __init__(self, fn): + self._fn = fn + + def __get__(self, instance, cls=None): + if self.result_key in vars(self): + return vars(self)[self.result_key] + else: + result = self._fn() + setattr(self, self.result_key, result) + return result + + +PRIMITIVE_TYPES = (list, float, int, bool, datetime, date, str, file_type) + +def allows_single_value_input(cls): + """ + This function returns True if the input composed schema model or any + descendant model allows a value only input + This is true for cases where oneOf contains items like: + oneOf: + - float + - NumberWithValidation + - StringEnum + - ArrayModel + - null + TODO: lru_cache this + """ + if ( + issubclass(cls, ModelSimple) or + cls in PRIMITIVE_TYPES + ): + return True + elif issubclass(cls, ModelComposed): + if not cls._composed_schemas['oneOf']: + return False + return any(allows_single_value_input(c) for c in cls._composed_schemas['oneOf']) + return False + +def composed_model_input_classes(cls): + """ + This function returns a list of the possible models that can be accepted as + inputs. + TODO: lru_cache this + """ + if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: + return [cls] + elif issubclass(cls, ModelNormal): + if cls.discriminator is None: + return [cls] + else: + return get_discriminated_classes(cls) + elif issubclass(cls, ModelComposed): + if not cls._composed_schemas['oneOf']: + return [] + if cls.discriminator is None: + input_classes = [] + for c in cls._composed_schemas['oneOf']: + input_classes.extend(composed_model_input_classes(c)) + return input_classes + else: + return get_discriminated_classes(cls) + return [] + + +class OpenApiModel(object): + """The base class for all OpenAPIModels""" + + def set_attribute(self, name, value): + # this is only used to set properties on self + + path_to_item = [] + if self._path_to_item: + path_to_item.extend(self._path_to_item) + path_to_item.append(name) + + if name in self.openapi_types: + required_types_mixed = self.openapi_types[name] + elif self.additional_properties_type is None: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + path_to_item + ) + elif self.additional_properties_type is not None: + required_types_mixed = self.additional_properties_type + + if get_simple_class(name) != str: + error_msg = type_error_message( + var_name=name, + var_value=name, + valid_classes=(str,), + key_type=True + ) + raise ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=(str,), + key_type=True + ) + + if self._check_type: + value = validate_and_convert_types( + value, required_types_mixed, path_to_item, self._spec_property_naming, + self._check_type, configuration=self._configuration) + if (name,) in self.allowed_values: + check_allowed_values( + self.allowed_values, + (name,), + value + ) + if (name,) in self.validations: + check_validations( + self.validations, + (name,), + value, + self._configuration + ) + self.__dict__['_data_store'][name] = value + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other + + def __setattr__(self, attr, value): + """set the value of an attribute using dot notation: `instance.attr = val`""" + self[attr] = value + + def __getattr__(self, attr): + """get the value of an attribute using dot notation: `instance.attr`""" + return self.__getitem__(attr) + + def __new__(cls, *args, **kwargs): + # this function uses the discriminator to + # pick a new schema/class to instantiate because a discriminator + # propertyName value was passed in + + if len(args) == 1: + arg = args[0] + if arg is None and is_type_nullable(cls): + # The input data is the 'null' value and the type is nullable. + return None + + if issubclass(cls, ModelComposed) and allows_single_value_input(cls): + model_kwargs = {} + oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) + return oneof_instance + + + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) + if ( + cls.discriminator is None or + cls in visited_composed_classes + ): + # Use case 1: this openapi schema (cls) does not have a discriminator + # Use case 2: we have already visited this class before and are sure that we + # want to instantiate it this time. We have visited this class deserializing + # a payload with a discriminator. During that process we traveled through + # this class but did not make an instance of it. Now we are making an + # instance of a composed class which contains cls in it, so this time make an instance of cls. + # + # Here's an example of use case 2: If Animal has a discriminator + # petType and we pass in "Dog", and the class Dog + # allOf includes Animal, we move through Animal + # once using the discriminator, and pick Dog. + # Then in the composed schema dog Dog, we will make an instance of the + # Animal class (because Dal has allOf: Animal) but this time we won't travel + # through Animal's discriminator because we passed in + # _visited_composed_classes = (Animal,) + + return super(OpenApiModel, cls).__new__(cls) + + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) + if new_cls is None: + path_to_item = kwargs.get('_path_to_item', ()) + disc_prop_value = kwargs.get( + discr_propertyname_js, kwargs.get(discr_propertyname_py)) + raise ApiValueError( + "Cannot deserialize input data due to invalid discriminator " + "value. The OpenAPI document has no mapping for discriminator " + "property '%s'='%s' at path: %s" % + (discr_propertyname_js, disc_prop_value, path_to_item) + ) + + if new_cls in visited_composed_classes: + # if we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here + return super(OpenApiModel, cls).__new__(cls) + + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + oneof_anyof_child = new_cls in oneof_anyof_classes + kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) + + if cls._composed_schemas.get('allOf') and oneof_anyof_child: + # Validate that we can make self because when we make the + # new_cls it will not include the allOf validations in self + self_inst = super(OpenApiModel, cls).__new__(cls) + self_inst.__init__(*args, **kwargs) + + new_inst = new_cls.__new__(new_cls, *args, **kwargs) + new_inst.__init__(*args, **kwargs) + return new_inst + + + @classmethod + @convert_js_args_to_python_args + def _new_from_openapi_data(cls, *args, **kwargs): + # this function uses the discriminator to + # pick a new schema/class to instantiate because a discriminator + # propertyName value was passed in + + if len(args) == 1: + arg = args[0] + if arg is None and is_type_nullable(cls): + # The input data is the 'null' value and the type is nullable. + return None + + if issubclass(cls, ModelComposed) and allows_single_value_input(cls): + model_kwargs = {} + oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) + return oneof_instance + + + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) + if ( + cls.discriminator is None or + cls in visited_composed_classes + ): + # Use case 1: this openapi schema (cls) does not have a discriminator + # Use case 2: we have already visited this class before and are sure that we + # want to instantiate it this time. We have visited this class deserializing + # a payload with a discriminator. During that process we traveled through + # this class but did not make an instance of it. Now we are making an + # instance of a composed class which contains cls in it, so this time make an instance of cls. + # + # Here's an example of use case 2: If Animal has a discriminator + # petType and we pass in "Dog", and the class Dog + # allOf includes Animal, we move through Animal + # once using the discriminator, and pick Dog. + # Then in the composed schema dog Dog, we will make an instance of the + # Animal class (because Dal has allOf: Animal) but this time we won't travel + # through Animal's discriminator because we passed in + # _visited_composed_classes = (Animal,) + + return cls._from_openapi_data(*args, **kwargs) + + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) + if new_cls is None: + path_to_item = kwargs.get('_path_to_item', ()) + disc_prop_value = kwargs.get( + discr_propertyname_js, kwargs.get(discr_propertyname_py)) + raise ApiValueError( + "Cannot deserialize input data due to invalid discriminator " + "value. The OpenAPI document has no mapping for discriminator " + "property '%s'='%s' at path: %s" % + (discr_propertyname_js, disc_prop_value, path_to_item) + ) + + if new_cls in visited_composed_classes: + # if we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here + return cls._from_openapi_data(*args, **kwargs) + + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + oneof_anyof_child = new_cls in oneof_anyof_classes + kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) + + if cls._composed_schemas.get('allOf') and oneof_anyof_child: + # Validate that we can make self because when we make the + # new_cls it will not include the allOf validations in self + self_inst = cls._from_openapi_data(*args, **kwargs) + + + new_inst = new_cls._new_from_openapi_data(*args, **kwargs) + return new_inst + + +class ModelSimple(OpenApiModel): + """the parent class of models whose type != object in their + swagger/openapi""" + + def __setitem__(self, name, value): + """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" + if name in self.required_properties: + self.__dict__[name] = value + return + + self.set_attribute(name, value) + + def get(self, name, default=None): + """returns the value of an attribute or some default value if the attribute was not set""" + if name in self.required_properties: + return self.__dict__[name] + + return self.__dict__['_data_store'].get(name, default) + + def __getitem__(self, name): + """get the value of an attribute using square-bracket notation: `instance[attr]`""" + if name in self: + return self.get(name) + + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + [e for e in [self._path_to_item, name] if e] + ) + + def __contains__(self, name): + """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" + if name in self.required_properties: + return name in self.__dict__ + + return name in self.__dict__['_data_store'] + + def to_str(self): + """Returns the string representation of the model""" + return str(self.value) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + this_val = self._data_store['value'] + that_val = other._data_store['value'] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + return vals_equal + + +class ModelNormal(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi""" + + def __setitem__(self, name, value): + """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" + if name in self.required_properties: + self.__dict__[name] = value + return + + self.set_attribute(name, value) + + def get(self, name, default=None): + """returns the value of an attribute or some default value if the attribute was not set""" + if name in self.required_properties: + return self.__dict__[name] + + return self.__dict__['_data_store'].get(name, default) + + def __getitem__(self, name): + """get the value of an attribute using square-bracket notation: `instance[attr]`""" + if name in self: + return self.get(name) + + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + [e for e in [self._path_to_item, name] if e] + ) + + def __contains__(self, name): + """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" + if name in self.required_properties: + return name in self.__dict__ + + return name in self.__dict__['_data_store'] + + def to_dict(self): + """Returns the model properties as a dict""" + return model_to_dict(self, serialize=False) + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + if not set(self._data_store.keys()) == set(other._data_store.keys()): + return False + for _var_name, this_val in self._data_store.items(): + that_val = other._data_store[_var_name] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if not vals_equal: + return False + return True + + +class ModelComposed(OpenApiModel): + """the parent class of models whose type == object in their + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ + + def __setitem__(self, name, value): + """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" + if name in self.required_properties: + self.__dict__[name] = value + return + + """ + Use cases: + 1. additional_properties_type is None (additionalProperties == False in spec) + Check for property presence in self.openapi_types + if not present then throw an error + if present set in self, set attribute + always set on composed schemas + 2. additional_properties_type exists + set attribute on self + always set on composed schemas + """ + if self.additional_properties_type is None: + """ + For an attribute to exist on a composed schema it must: + - fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND + - fulfill schema_requirements in each oneOf/anyOf/allOf schemas + + schema_requirements: + For an attribute to exist on a schema it must: + - be present in properties at the schema OR + - have additionalProperties unset (defaults additionalProperties = any type) OR + - have additionalProperties set + """ + if name not in self.openapi_types: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + [e for e in [self._path_to_item, name] if e] + ) + # attribute must be set on self and composed instances + self.set_attribute(name, value) + for model_instance in self._composed_instances: + setattr(model_instance, name, value) + if name not in self._var_name_to_model_instances: + # we assigned an additional property + self.__dict__['_var_name_to_model_instances'][name] = self._composed_instances + [self] + return None + + __unset_attribute_value__ = object() + + def get(self, name, default=None): + """returns the value of an attribute or some default value if the attribute was not set""" + if name in self.required_properties: + return self.__dict__[name] + + # get the attribute from the correct instance + model_instances = self._var_name_to_model_instances.get(name) + values = [] + # A composed model stores self and child (oneof/anyOf/allOf) models under + # self._var_name_to_model_instances. + # Any property must exist in self and all model instances + # The value stored in all model instances must be the same + if model_instances: + for model_instance in model_instances: + if name in model_instance._data_store: + v = model_instance._data_store[name] + if v not in values: + values.append(v) + len_values = len(values) + if len_values == 0: + return default + elif len_values == 1: + return values[0] + elif len_values > 1: + raise ApiValueError( + "Values stored for property {0} in {1} differ when looking " + "at self and self's composed instances. All values must be " + "the same".format(name, type(self).__name__), + [e for e in [self._path_to_item, name] if e] + ) + + def __getitem__(self, name): + """get the value of an attribute using square-bracket notation: `instance[attr]`""" + value = self.get(name, self.__unset_attribute_value__) + if value is self.__unset_attribute_value__: + raise ApiAttributeError( + "{0} has no attribute '{1}'".format( + type(self).__name__, name), + [e for e in [self._path_to_item, name] if e] + ) + return value + + def __contains__(self, name): + """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" + + if name in self.required_properties: + return name in self.__dict__ + + model_instances = self._var_name_to_model_instances.get( + name, self._additional_properties_model_instances) + + if model_instances: + for model_instance in model_instances: + if name in model_instance._data_store: + return True + + return False + + def to_dict(self): + """Returns the model properties as a dict""" + return model_to_dict(self, serialize=False) + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, self.__class__): + return False + + if not set(self._data_store.keys()) == set(other._data_store.keys()): + return False + for _var_name, this_val in self._data_store.items(): + that_val = other._data_store[_var_name] + types = set() + types.add(this_val.__class__) + types.add(that_val.__class__) + vals_equal = this_val == that_val + if not vals_equal: + return False + return True + + +COERCION_INDEX_BY_TYPE = { + ModelComposed: 0, + ModelNormal: 1, + ModelSimple: 2, + none_type: 3, # The type of 'None'. + list: 4, + dict: 5, + float: 6, + int: 7, + bool: 8, + datetime: 9, + date: 10, + str: 11, + file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. +} + +# these are used to limit what type conversions we try to do +# when we have a valid type already and we want to try converting +# to another type +UPCONVERSION_TYPE_PAIRS = ( + (str, datetime), + (str, date), + (int, float), # A float may be serialized as an integer, e.g. '3' is a valid serialized float. + (list, ModelComposed), + (dict, ModelComposed), + (str, ModelComposed), + (int, ModelComposed), + (float, ModelComposed), + (list, ModelComposed), + (list, ModelNormal), + (dict, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), +) + +COERCIBLE_TYPE_PAIRS = { + False: ( # client instantiation of a model with client data + # (dict, ModelComposed), + # (list, ModelComposed), + # (dict, ModelNormal), + # (list, ModelNormal), + # (str, ModelSimple), + # (int, ModelSimple), + # (float, ModelSimple), + # (list, ModelSimple), + # (str, int), + # (str, float), + # (str, datetime), + # (str, date), + # (int, str), + # (float, str), + ), + True: ( # server -> client data + (dict, ModelComposed), + (list, ModelComposed), + (dict, ModelNormal), + (list, ModelNormal), + (str, ModelSimple), + (int, ModelSimple), + (float, ModelSimple), + (list, ModelSimple), + # (str, int), + # (str, float), + (str, datetime), + (str, date), + # (int, str), + # (float, str), + (str, file_type) + ), +} + + +def get_simple_class(input_value): + """Returns an input_value's simple class that we will use for type checking + Python2: + float and int will return int, where int is the python3 int backport + str and unicode will return str, where str is the python3 str backport + Note: float and int ARE both instances of int backport + Note: str_py2 and unicode_py2 are NOT both instances of str backport + + Args: + input_value (class/class_instance): the item for which we will return + the simple class + """ + if isinstance(input_value, type): + # input_value is a class + return input_value + elif isinstance(input_value, tuple): + return tuple + elif isinstance(input_value, list): + return list + elif isinstance(input_value, dict): + return dict + elif isinstance(input_value, none_type): + return none_type + elif isinstance(input_value, file_type): + return file_type + elif isinstance(input_value, bool): + # this must be higher than the int check because + # isinstance(True, int) == True + return bool + elif isinstance(input_value, int): + return int + elif isinstance(input_value, datetime): + # this must be higher than the date check because + # isinstance(datetime_instance, date) == True + return datetime + elif isinstance(input_value, date): + return date + elif isinstance(input_value, str): + return str + return type(input_value) + + +def check_allowed_values(allowed_values, input_variable_path, input_values): + """Raises an exception if the input_values are not allowed + + Args: + allowed_values (dict): the allowed_values dict + input_variable_path (tuple): the path to the input variable + input_values (list/str/int/float/date/datetime): the values that we + are checking to see if they are in allowed_values + """ + these_allowed_values = list(allowed_values[input_variable_path].values()) + if (isinstance(input_values, list) + and not set(input_values).issubset( + set(these_allowed_values))): + invalid_values = ", ".join( + map(str, set(input_values) - set(these_allowed_values))), + raise ApiValueError( + "Invalid values for `%s` [%s], must be a subset of [%s]" % + ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)) + ) + ) + elif (isinstance(input_values, dict) + and not set( + input_values.keys()).issubset(set(these_allowed_values))): + invalid_values = ", ".join( + map(str, set(input_values.keys()) - set(these_allowed_values))) + raise ApiValueError( + "Invalid keys in `%s` [%s], must be a subset of [%s]" % + ( + input_variable_path[0], + invalid_values, + ", ".join(map(str, these_allowed_values)) + ) + ) + elif (not isinstance(input_values, (list, dict)) + and input_values not in these_allowed_values): + raise ApiValueError( + "Invalid value for `%s` (%s), must be one of %s" % + ( + input_variable_path[0], + input_values, + these_allowed_values + ) + ) + + +def is_json_validation_enabled(schema_keyword, configuration=None): + """Returns true if JSON schema validation is enabled for the specified + validation keyword. This can be used to skip JSON schema structural validation + as requested in the configuration. + + Args: + schema_keyword (string): the name of a JSON schema validation keyword. + configuration (Configuration): the configuration class. + """ + + return (configuration is None or + not hasattr(configuration, '_disabled_client_side_validations') or + schema_keyword not in configuration._disabled_client_side_validations) + + +def check_validations( + validations, input_variable_path, input_values, + configuration=None): + """Raises an exception if the input_values are invalid + + Args: + validations (dict): the validation dictionary. + input_variable_path (tuple): the path to the input variable. + input_values (list/str/int/float/date/datetime): the values that we + are checking. + configuration (Configuration): the configuration class. + """ + + if input_values is None: + return + + current_validations = validations[input_variable_path] + if (is_json_validation_enabled('multipleOf', configuration) and + 'multiple_of' in current_validations and + isinstance(input_values, (int, float)) and + not (float(input_values) / current_validations['multiple_of']).is_integer()): + # Note 'multipleOf' will be as good as the floating point arithmetic. + raise ApiValueError( + "Invalid value for `%s`, value must be a multiple of " + "`%s`" % ( + input_variable_path[0], + current_validations['multiple_of'] + ) + ) + + if (is_json_validation_enabled('maxLength', configuration) and + 'max_length' in current_validations and + len(input_values) > current_validations['max_length']): + raise ApiValueError( + "Invalid value for `%s`, length must be less than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['max_length'] + ) + ) + + if (is_json_validation_enabled('minLength', configuration) and + 'min_length' in current_validations and + len(input_values) < current_validations['min_length']): + raise ApiValueError( + "Invalid value for `%s`, length must be greater than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['min_length'] + ) + ) + + if (is_json_validation_enabled('maxItems', configuration) and + 'max_items' in current_validations and + len(input_values) > current_validations['max_items']): + raise ApiValueError( + "Invalid value for `%s`, number of items must be less than or " + "equal to `%s`" % ( + input_variable_path[0], + current_validations['max_items'] + ) + ) + + if (is_json_validation_enabled('minItems', configuration) and + 'min_items' in current_validations and + len(input_values) < current_validations['min_items']): + raise ValueError( + "Invalid value for `%s`, number of items must be greater than or " + "equal to `%s`" % ( + input_variable_path[0], + current_validations['min_items'] + ) + ) + + items = ('exclusive_maximum', 'inclusive_maximum', 'exclusive_minimum', + 'inclusive_minimum') + if (any(item in current_validations for item in items)): + if isinstance(input_values, list): + max_val = max(input_values) + min_val = min(input_values) + elif isinstance(input_values, dict): + max_val = max(input_values.values()) + min_val = min(input_values.values()) + else: + max_val = input_values + min_val = input_values + + if (is_json_validation_enabled('exclusiveMaximum', configuration) and + 'exclusive_maximum' in current_validations and + max_val >= current_validations['exclusive_maximum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than `%s`" % ( + input_variable_path[0], + current_validations['exclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('maximum', configuration) and + 'inclusive_maximum' in current_validations and + max_val > current_validations['inclusive_maximum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value less than or equal to " + "`%s`" % ( + input_variable_path[0], + current_validations['inclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('exclusiveMinimum', configuration) and + 'exclusive_minimum' in current_validations and + min_val <= current_validations['exclusive_minimum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than `%s`" % + ( + input_variable_path[0], + current_validations['exclusive_maximum'] + ) + ) + + if (is_json_validation_enabled('minimum', configuration) and + 'inclusive_minimum' in current_validations and + min_val < current_validations['inclusive_minimum']): + raise ApiValueError( + "Invalid value for `%s`, must be a value greater than or equal " + "to `%s`" % ( + input_variable_path[0], + current_validations['inclusive_minimum'] + ) + ) + flags = current_validations.get('regex', {}).get('flags', 0) + if (is_json_validation_enabled('pattern', configuration) and + 'regex' in current_validations and + not re.search(current_validations['regex']['pattern'], + input_values, flags=flags)): + err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( + input_variable_path[0], + current_validations['regex']['pattern'] + ) + if flags != 0: + # Don't print the regex flags if the flags are not + # specified in the OAS document. + err_msg = r"%s with flags=`%s`" % (err_msg, flags) + raise ApiValueError(err_msg) + + +def order_response_types(required_types): + """Returns the required types sorted in coercion order + + Args: + required_types (list/tuple): collection of classes or instance of + list or dict with class information inside it. + + Returns: + (list): coercion order sorted collection of classes or instance + of list or dict with class information inside it. + """ + + def index_getter(class_or_instance): + if isinstance(class_or_instance, list): + return COERCION_INDEX_BY_TYPE[list] + elif isinstance(class_or_instance, dict): + return COERCION_INDEX_BY_TYPE[dict] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelComposed)): + return COERCION_INDEX_BY_TYPE[ModelComposed] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelNormal)): + return COERCION_INDEX_BY_TYPE[ModelNormal] + elif (inspect.isclass(class_or_instance) + and issubclass(class_or_instance, ModelSimple)): + return COERCION_INDEX_BY_TYPE[ModelSimple] + elif class_or_instance in COERCION_INDEX_BY_TYPE: + return COERCION_INDEX_BY_TYPE[class_or_instance] + raise ApiValueError("Unsupported type: %s" % class_or_instance) + + sorted_types = sorted( + required_types, + key=lambda class_or_instance: index_getter(class_or_instance) + ) + return sorted_types + + +def remove_uncoercible(required_types_classes, current_item, spec_property_naming, + must_convert=True): + """Only keeps the type conversions that are possible + + Args: + required_types_classes (tuple): tuple of classes that are required + these should be ordered by COERCION_INDEX_BY_TYPE + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + current_item (any): the current item (input data) to be converted + + Keyword Args: + must_convert (bool): if True the item to convert is of the wrong + type and we want a big list of coercibles + if False, we want a limited list of coercibles + + Returns: + (list): the remaining coercible required types, classes only + """ + current_type_simple = get_simple_class(current_item) + + results_classes = [] + for required_type_class in required_types_classes: + # convert our models to OpenApiModel + required_type_class_simplified = required_type_class + if isinstance(required_type_class_simplified, type): + if issubclass(required_type_class_simplified, ModelComposed): + required_type_class_simplified = ModelComposed + elif issubclass(required_type_class_simplified, ModelNormal): + required_type_class_simplified = ModelNormal + elif issubclass(required_type_class_simplified, ModelSimple): + required_type_class_simplified = ModelSimple + + if required_type_class_simplified == current_type_simple: + # don't consider converting to one's own class + continue + + class_pair = (current_type_simple, required_type_class_simplified) + if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: + results_classes.append(required_type_class) + elif class_pair in UPCONVERSION_TYPE_PAIRS: + results_classes.append(required_type_class) + return results_classes + +def get_discriminated_classes(cls): + """ + Returns all the classes that a discriminator converts to + TODO: lru_cache this + """ + possible_classes = [] + key = list(cls.discriminator.keys())[0] + if is_type_nullable(cls): + possible_classes.append(cls) + for discr_cls in cls.discriminator[key].values(): + if hasattr(discr_cls, 'discriminator') and discr_cls.discriminator is not None: + possible_classes.extend(get_discriminated_classes(discr_cls)) + else: + possible_classes.append(discr_cls) + return possible_classes + + +def get_possible_classes(cls, from_server_context): + # TODO: lru_cache this + possible_classes = [cls] + if from_server_context: + return possible_classes + if hasattr(cls, 'discriminator') and cls.discriminator is not None: + possible_classes = [] + possible_classes.extend(get_discriminated_classes(cls)) + elif issubclass(cls, ModelComposed): + possible_classes.extend(composed_model_input_classes(cls)) + return possible_classes + + +def get_required_type_classes(required_types_mixed, spec_property_naming): + """Converts the tuple required_types into a tuple and a dict described + below + + Args: + required_types_mixed (tuple/list): will contain either classes or + instance of list or dict + spec_property_naming (bool): if True these values came from the + server, and we use the data types in our endpoints. + If False, we are client side and we need to include + oneOf and discriminator classes inside the data types in our endpoints + + Returns: + (valid_classes, dict_valid_class_to_child_types_mixed): + valid_classes (tuple): the valid classes that the current item + should be + dict_valid_class_to_child_types_mixed (dict): + valid_class (class): this is the key + child_types_mixed (list/dict/tuple): describes the valid child + types + """ + valid_classes = [] + child_req_types_by_current_type = {} + for required_type in required_types_mixed: + if isinstance(required_type, list): + valid_classes.append(list) + child_req_types_by_current_type[list] = required_type + elif isinstance(required_type, tuple): + valid_classes.append(tuple) + child_req_types_by_current_type[tuple] = required_type + elif isinstance(required_type, dict): + valid_classes.append(dict) + child_req_types_by_current_type[dict] = required_type[str] + else: + valid_classes.extend(get_possible_classes(required_type, spec_property_naming)) + return tuple(valid_classes), child_req_types_by_current_type + + +def change_keys_js_to_python(input_dict, model_class): + """ + Converts from javascript_key keys in the input_dict to python_keys in + the output dict using the mapping in model_class. + If the input_dict contains a key which does not declared in the model_class, + the key is added to the output dict as is. The assumption is the model_class + may have undeclared properties (additionalProperties attribute in the OAS + document). + """ + + if getattr(model_class, 'attribute_map', None) is None: + return input_dict + output_dict = {} + reversed_attr_map = {value: key for key, value in + model_class.attribute_map.items()} + for javascript_key, value in input_dict.items(): + python_key = reversed_attr_map.get(javascript_key) + if python_key is None: + # if the key is unknown, it is in error or it is an + # additionalProperties variable + python_key = javascript_key + output_dict[python_key] = value + return output_dict + + +def get_type_error(var_value, path_to_item, valid_classes, key_type=False): + error_msg = type_error_message( + var_name=path_to_item[-1], + var_value=var_value, + valid_classes=valid_classes, + key_type=key_type + ) + return ApiTypeError( + error_msg, + path_to_item=path_to_item, + valid_classes=valid_classes, + key_type=key_type + ) + + +def deserialize_primitive(data, klass, path_to_item): + """Deserializes string to primitive type. + + :param data: str/int/float + :param klass: str/class the class to convert to + + :return: int, float, str, bool, date, datetime + """ + additional_message = "" + try: + if klass in {datetime, date}: + additional_message = ( + "If you need your parameter to have a fallback " + "string value, please set its type as `type: {}` in your " + "spec. That allows the value to be any type. " + ) + if klass == datetime: + if len(data) < 8: + raise ValueError("This is not a datetime") + # The string should be in iso8601 datetime format. + parsed_datetime = parse(data) + date_only = ( + parsed_datetime.hour == 0 and + parsed_datetime.minute == 0 and + parsed_datetime.second == 0 and + parsed_datetime.tzinfo is None and + 8 <= len(data) <= 10 + ) + if date_only: + raise ValueError("This is a date, not a datetime") + return parsed_datetime + elif klass == date: + if len(data) < 8: + raise ValueError("This is not a date") + return parse(data).date() + else: + converted_value = klass(data) + if isinstance(data, str) and klass == float: + if str(converted_value) != data: + # '7' -> 7.0 -> '7.0' != '7' + raise ValueError('This is not a float') + return converted_value + except (OverflowError, ValueError) as ex: + # parse can raise OverflowError + raise ApiValueError( + "{0}Failed to parse {1} as {2}".format( + additional_message, repr(data), klass.__name__ + ), + path_to_item=path_to_item + ) from ex + + +def get_discriminator_class(model_class, + discr_name, + discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # So look in the ancestor or descendant discriminators + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat + # hierarchy, the discriminator mappings may be defined at any level + # in the hierarchy. + # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig + # if we try to make BasquePig from mammal, we need to travel through + # the oneOf descendant discriminators to find BasquePig + descendant_classes = model_class._composed_schemas.get('oneOf', ()) + \ + model_class._composed_schemas.get('anyOf', ()) + ancestor_classes = model_class._composed_schemas.get('allOf', ()) + possible_classes = descendant_classes + ancestor_classes + for cls in possible_classes: + # Check if the schema has inherited discriminators. + if hasattr(cls, 'discriminator') and cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited) + if used_model_class is not None: + return used_model_class + return used_model_class + + +def deserialize_model(model_data, model_class, path_to_item, check_type, + configuration, spec_property_naming): + """Deserializes model_data to model instance. + + Args: + model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model + model_class (OpenApiModel): the model class + path_to_item (list): path to the model in the received data + check_type (bool): whether to check the data tupe for the values in + the model + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + + Returns: + model instance + + Raise: + ApiTypeError + ApiValueError + ApiKeyError + """ + + kw_args = dict(_check_type=check_type, + _path_to_item=path_to_item, + _configuration=configuration, + _spec_property_naming=spec_property_naming) + + if issubclass(model_class, ModelSimple): + return model_class._new_from_openapi_data(model_data, **kw_args) + elif isinstance(model_data, list): + return model_class._new_from_openapi_data(*model_data, **kw_args) + if isinstance(model_data, dict): + kw_args.update(model_data) + return model_class._new_from_openapi_data(**kw_args) + elif isinstance(model_data, PRIMITIVE_TYPES): + return model_class._new_from_openapi_data(model_data, **kw_args) + + +def deserialize_file(response_data, configuration, content_disposition=None): + """Deserializes body to file + + Saves response body into a file in a temporary folder, + using the filename from the `Content-Disposition` header if provided. + + Args: + param response_data (str): the file data to write + configuration (Configuration): the instance to use to convert files + + Keyword Args: + content_disposition (str): the value of the Content-Disposition + header + + Returns: + (file_type): the deserialized file which is open + The user is responsible for closing and reading the file + """ + fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) + os.close(fd) + os.remove(path) + + if content_disposition: + filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', + content_disposition).group(1) + path = os.path.join(os.path.dirname(path), filename) + + with open(path, "wb") as f: + if isinstance(response_data, str): + # change str to bytes so we can write it + response_data = response_data.encode('utf-8') + f.write(response_data) + + f = open(path, "rb") + return f + + +def attempt_convert_item(input_value, valid_classes, path_to_item, + configuration, spec_property_naming, key_type=False, + must_convert=False, check_type=True): + """ + Args: + input_value (any): the data to convert + valid_classes (any): the classes that are valid + path_to_item (list): the path to the item to convert + configuration (Configuration): the instance to use to convert files + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + key_type (bool): if True we need to convert a key type (not supported) + must_convert (bool): if True we must convert + check_type (bool): if True we check the type or the returned data in + ModelComposed/ModelNormal/ModelSimple instances + + Returns: + instance (any) the fixed item + + Raises: + ApiTypeError + ApiValueError + ApiKeyError + """ + valid_classes_ordered = order_response_types(valid_classes) + valid_classes_coercible = remove_uncoercible( + valid_classes_ordered, input_value, spec_property_naming) + if not valid_classes_coercible or key_type: + # we do not handle keytype errors, json will take care + # of this for us + if configuration is None or not configuration.discard_unknown_keys: + raise get_type_error(input_value, path_to_item, valid_classes, + key_type=key_type) + for valid_class in valid_classes_coercible: + try: + if issubclass(valid_class, OpenApiModel): + return deserialize_model(input_value, valid_class, + path_to_item, check_type, + configuration, spec_property_naming) + elif valid_class == file_type: + return deserialize_file(input_value, configuration) + return deserialize_primitive(input_value, valid_class, + path_to_item) + except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: + if must_convert: + raise conversion_exc + # if we have conversion errors when must_convert == False + # we ignore the exception and move on to the next class + continue + # we were unable to convert, must_convert == False + return input_value + + +def is_type_nullable(input_type): + """ + Returns true if None is an allowed value for the specified input_type. + + A type is nullable if at least one of the following conditions is true: + 1. The OAS 'nullable' attribute has been specified, + 1. The type is the 'null' type, + 1. The type is a anyOf/oneOf composed schema, and a child schema is + the 'null' type. + Args: + input_type (type): the class of the input_value that we are + checking + Returns: + bool + """ + if input_type is none_type: + return True + if issubclass(input_type, OpenApiModel) and input_type._nullable: + return True + if issubclass(input_type, ModelComposed): + # If oneOf/anyOf, check if the 'null' type is one of the allowed types. + for t in input_type._composed_schemas.get('oneOf', ()): + if is_type_nullable(t): return True + for t in input_type._composed_schemas.get('anyOf', ()): + if is_type_nullable(t): return True + return False + + +def is_valid_type(input_class_simple, valid_classes): + """ + Args: + input_class_simple (class): the class of the input_value that we are + checking + valid_classes (tuple): the valid classes that the current item + should be + Returns: + bool + """ + valid_type = input_class_simple in valid_classes + if not valid_type and ( + issubclass(input_class_simple, OpenApiModel) or + input_class_simple is none_type): + for valid_class in valid_classes: + if input_class_simple is none_type and is_type_nullable(valid_class): + # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. + return True + if not (issubclass(valid_class, OpenApiModel) and valid_class.discriminator): + continue + discr_propertyname_py = list(valid_class.discriminator.keys())[0] + discriminator_classes = ( + valid_class.discriminator[discr_propertyname_py].values() + ) + valid_type = is_valid_type(input_class_simple, discriminator_classes) + if valid_type: + return True + return valid_type + + +def validate_and_convert_types(input_value, required_types_mixed, path_to_item, + spec_property_naming, _check_type, configuration=None): + """Raises a TypeError is there is a problem, otherwise returns value + + Args: + input_value (any): the data to validate/convert + required_types_mixed (list/dict/tuple): A list of + valid classes, or a list tuples of valid classes, or a dict where + the value is a tuple of value classes + path_to_item: (list) the path to the data being validated + this stores a list of keys or indices to get to the data being + validated + spec_property_naming (bool): True if the variable names in the input + data are serialized names as specified in the OpenAPI document. + False if the variables names in the input data are python + variable names in PEP-8 snake case. + _check_type: (boolean) if true, type will be checked and conversion + will be attempted. + configuration: (Configuration): the configuration class to use + when converting file_type items. + If passed, conversion will be attempted when possible + If not passed, no conversions will be attempted and + exceptions will be raised + + Returns: + the correctly typed value + + Raises: + ApiTypeError + """ + results = get_required_type_classes(required_types_mixed, spec_property_naming) + valid_classes, child_req_types_by_current_type = results + + input_class_simple = get_simple_class(input_value) + valid_type = is_valid_type(input_class_simple, valid_classes) + if not valid_type: + if configuration: + # if input_value is not valid_type try to convert it + converted_instance = attempt_convert_item( + input_value, + valid_classes, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=True, + check_type=_check_type + ) + return converted_instance + else: + raise get_type_error(input_value, path_to_item, valid_classes, + key_type=False) + + # input_value's type is in valid_classes + if len(valid_classes) > 1 and configuration: + # there are valid classes which are not the current class + valid_classes_coercible = remove_uncoercible( + valid_classes, input_value, spec_property_naming, must_convert=False) + if valid_classes_coercible: + converted_instance = attempt_convert_item( + input_value, + valid_classes_coercible, + path_to_item, + configuration, + spec_property_naming, + key_type=False, + must_convert=False, + check_type=_check_type + ) + return converted_instance + + if child_req_types_by_current_type == {}: + # all types are of the required types and there are no more inner + # variables left to look at + return input_value + inner_required_types = child_req_types_by_current_type.get( + type(input_value) + ) + if inner_required_types is None: + # for this type, there are not more inner variables left to look at + return input_value + if isinstance(input_value, list): + if input_value == []: + # allow an empty list + return input_value + for index, inner_value in enumerate(input_value): + inner_path = list(path_to_item) + inner_path.append(index) + input_value[index] = validate_and_convert_types( + inner_value, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration + ) + elif isinstance(input_value, dict): + if input_value == {}: + # allow an empty dict + return input_value + for inner_key, inner_val in input_value.items(): + inner_path = list(path_to_item) + inner_path.append(inner_key) + if get_simple_class(inner_key) != str: + raise get_type_error(inner_key, inner_path, valid_classes, + key_type=True) + input_value[inner_key] = validate_and_convert_types( + inner_val, + inner_required_types, + inner_path, + spec_property_naming, + _check_type, + configuration=configuration + ) + return input_value + + +def model_to_dict(model_instance, serialize=True): + """Returns the model properties as a dict + + Args: + model_instance (one of your model instances): the model instance that + will be converted to a dict. + + Keyword Args: + serialize (bool): if True, the keys in the dict will be values from + attribute_map + """ + result = {} + + model_instances = [model_instance] + if model_instance._composed_schemas: + model_instances.extend(model_instance._composed_instances) + seen_json_attribute_names = set() + used_fallback_python_attribute_names = set() + py_to_json_map = {} + for model_instance in model_instances: + for attr, value in model_instance._data_store.items(): + if serialize: + # we use get here because additional property key names do not + # exist in attribute_map + try: + attr = model_instance.attribute_map[attr] + py_to_json_map.update(model_instance.attribute_map) + seen_json_attribute_names.add(attr) + except KeyError: + used_fallback_python_attribute_names.add(attr) + if isinstance(value, list): + if not value: + # empty list or None + result[attr] = value + else: + res = [] + for v in value: + if isinstance(v, PRIMITIVE_TYPES) or v is None: + res.append(v) + elif isinstance(v, ModelSimple): + res.append(v.value) + else: + res.append(model_to_dict(v, serialize=serialize)) + result[attr] = res + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], + model_to_dict(item[1], serialize=serialize)) + if hasattr(item[1], '_data_store') else item, + value.items() + )) + elif isinstance(value, ModelSimple): + result[attr] = value.value + elif hasattr(value, '_data_store'): + result[attr] = model_to_dict(value, serialize=serialize) + else: + result[attr] = value + if serialize: + for python_key in used_fallback_python_attribute_names: + json_key = py_to_json_map.get(python_key) + if json_key is None: + continue + if python_key == json_key: + continue + json_key_assigned_no_need_for_python_key = json_key in seen_json_attribute_names + if json_key_assigned_no_need_for_python_key: + del result[python_key] + + return result + + +def type_error_message(var_value=None, var_name=None, valid_classes=None, + key_type=None): + """ + Keyword Args: + var_value (any): the variable which has the type_error + var_name (str): the name of the variable which has the typ error + valid_classes (tuple): the accepted classes for current_item's + value + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + """ + key_or_value = 'value' + if key_type: + key_or_value = 'key' + valid_classes_phrase = get_valid_classes_phrase(valid_classes) + msg = ( + "Invalid type for variable '{0}'. Required {1} type {2} and " + "passed type was {3}".format( + var_name, + key_or_value, + valid_classes_phrase, + type(var_value).__name__, + ) + ) + return msg + + +def get_valid_classes_phrase(input_classes): + """Returns a string phrase describing what types are allowed + """ + all_classes = list(input_classes) + all_classes = sorted(all_classes, key=lambda cls: cls.__name__) + all_class_names = [cls.__name__ for cls in all_classes] + if len(all_class_names) == 1: + return 'is {0}'.format(all_class_names[0]) + return "is one of [{0}]".format(", ".join(all_class_names)) + + +def get_allof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + used to make instances + constant_args (dict): + metadata arguments: + _check_type + _path_to_item + _spec_property_naming + _configuration + _visited_composed_classes + + Returns + composed_instances (list) + """ + composed_instances = [] + for allof_class in self._composed_schemas['allOf']: + + try: + allof_instance = allof_class(**model_args, **constant_args) + composed_instances.append(allof_instance) + except Exception as ex: + raise ApiValueError( + "Invalid inputs given to generate an instance of '%s'. The " + "input data was invalid for the allOf schema '%s' in the composed " + "schema '%s'. Error=%s" % ( + allof_class.__name__, + allof_class.__name__, + self.__class__.__name__, + str(ex) + ) + ) from ex + return composed_instances + + +def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None): + """ + Find the oneOf schema that matches the input data (e.g. payload). + If exactly one schema matches the input data, an instance of that schema + is returned. + If zero or more than one schema match the input data, an exception is raised. + In OAS 3.x, the payload MUST, by validation, match exactly one of the + schemas described by oneOf. + + Args: + cls: the class we are handling + model_kwargs (dict): var_name to var_value + The input data, e.g. the payload that must match a oneOf schema + in the OpenAPI document. + constant_kwargs (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Kwargs: + model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): + the value to assign to a primitive class or ModelSimple class + Notes: + - this is only passed in when oneOf includes types which are not object + - None is used to suppress handling of model_arg, nullable models are handled in __new__ + + Returns + oneof_instance (instance) + """ + if len(cls._composed_schemas['oneOf']) == 0: + return None + + oneof_instances = [] + # Iterate over each oneOf schema and determine if the input data + # matches the oneOf schemas. + for oneof_class in cls._composed_schemas['oneOf']: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if oneof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + single_value_input = allows_single_value_input(oneof_class) + + try: + if not single_value_input: + oneof_instance = oneof_class(**model_kwargs, **constant_kwargs) + else: + if issubclass(oneof_class, ModelSimple): + oneof_instance = oneof_class(model_arg, **constant_kwargs) + elif oneof_class in PRIMITIVE_TYPES: + oneof_instance = validate_and_convert_types( + model_arg, + (oneof_class,), + constant_kwargs['_path_to_item'], + constant_kwargs['_spec_property_naming'], + constant_kwargs['_check_type'], + configuration=constant_kwargs['_configuration'] + ) + oneof_instances.append(oneof_instance) + except Exception: + pass + if len(oneof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None " + "of the oneOf schemas matched the input data." % + cls.__name__ + ) + elif len(oneof_instances) > 1: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. Multiple " + "oneOf schemas matched the inputs, but a max of one is allowed." % + cls.__name__ + ) + return oneof_instances[0] + + +def get_anyof_instances(self, model_args, constant_args): + """ + Args: + self: the class we are handling + model_args (dict): var_name to var_value + The input data, e.g. the payload that must match at least one + anyOf child schema in the OpenAPI document. + constant_args (dict): var_name to var_value + args that every model requires, including configuration, server + and path to item. + + Returns + anyof_instances (list) + """ + anyof_instances = [] + if len(self._composed_schemas['anyOf']) == 0: + return anyof_instances + + for anyof_class in self._composed_schemas['anyOf']: + # The composed oneOf schema allows the 'null' type and the input data + # is the null value. This is a OAS >= 3.1 feature. + if anyof_class is none_type: + # skip none_types because we are deserializing dict data. + # none_type deserialization is handled in the __new__ method + continue + + try: + anyof_instance = anyof_class(**model_args, **constant_args) + anyof_instances.append(anyof_instance) + except Exception: + pass + if len(anyof_instances) == 0: + raise ApiValueError( + "Invalid inputs given to generate an instance of %s. None of the " + "anyOf schemas matched the inputs." % + self.__class__.__name__ + ) + return anyof_instances + + +def get_discarded_args(self, composed_instances, model_args): + """ + Gathers the args that were discarded by configuration.discard_unknown_keys + """ + model_arg_keys = model_args.keys() + discarded_args = set() + # arguments passed to self were already converted to python names + # before __init__ was called + for instance in composed_instances: + if instance.__class__ in self._composed_schemas['allOf']: + try: + keys = instance.to_dict().keys() + discarded_keys = model_args - keys + discarded_args.update(discarded_keys) + except Exception: + # allOf integer schema will throw exception + pass + else: + try: + all_keys = set(model_to_dict(instance, serialize=False).keys()) + js_keys = model_to_dict(instance, serialize=True).keys() + all_keys.update(js_keys) + discarded_keys = model_arg_keys - all_keys + discarded_args.update(discarded_keys) + except Exception: + # allOf integer schema will throw exception + pass + return discarded_args + + +def validate_get_composed_info(constant_args, model_args, self): + """ + For composed schemas, generate schema instances for + all schemas in the oneOf/anyOf/allOf definition. If additional + properties are allowed, also assign those properties on + all matched schemas that contain additionalProperties. + Openapi schemas are python classes. + + Exceptions are raised if: + - 0 or > 1 oneOf schema matches the model_args input data + - no anyOf schema matches the model_args input data + - any of the allOf schemas do not match the model_args input data + + Args: + constant_args (dict): these are the args that every model requires + model_args (dict): these are the required and optional spec args that + were passed in to make this model + self (class): the class that we are instantiating + This class contains self._composed_schemas + + Returns: + composed_info (list): length three + composed_instances (list): the composed instances which are not + self + var_name_to_model_instances (dict): a dict going from var_name + to the model_instance which holds that var_name + the model_instance may be self or an instance of one of the + classes in self.composed_instances() + additional_properties_model_instances (list): a list of the + model instances which have the property + additional_properties_type. This list can include self + """ + # create composed_instances + composed_instances = [] + allof_instances = get_allof_instances(self, model_args, constant_args) + composed_instances.extend(allof_instances) + oneof_instance = get_oneof_instance(self.__class__, model_args, constant_args) + if oneof_instance is not None: + composed_instances.append(oneof_instance) + anyof_instances = get_anyof_instances(self, model_args, constant_args) + composed_instances.extend(anyof_instances) + """ + set additional_properties_model_instances + additional properties must be evaluated at the schema level + so self's additional properties are most important + If self is a composed schema with: + - no properties defined in self + - additionalProperties: False + Then for object payloads every property is an additional property + and they are not allowed, so only empty dict is allowed + + Properties must be set on all matching schemas + so when a property is assigned toa composed instance, it must be set on all + composed instances regardless of additionalProperties presence + keeping it to prevent breaking changes in v5.0.1 + TODO remove cls._additional_properties_model_instances in 6.0.0 + """ + additional_properties_model_instances = [] + if self.additional_properties_type is not None: + additional_properties_model_instances = [self] + + """ + no need to set properties on self in here, they will be set in __init__ + By here all composed schema oneOf/anyOf/allOf instances have their properties set using + model_args + """ + discarded_args = get_discarded_args(self, composed_instances, model_args) + + # map variable names to composed_instances + var_name_to_model_instances = {} + for prop_name in model_args: + if prop_name not in discarded_args: + var_name_to_model_instances[prop_name] = [self] + composed_instances + + return [ + composed_instances, + var_name_to_model_instances, + additional_properties_model_instances, + discarded_args + ] diff --git a/web/src/services/bio_api/openapi/models/__init__.py b/web/src/services/bio_api/openapi/models/__init__.py new file mode 100644 index 00000000..0d3b9a19 --- /dev/null +++ b/web/src/services/bio_api/openapi/models/__init__.py @@ -0,0 +1,24 @@ +# flake8: noqa + +# import all models into this package +# if you have many models here with many references from one model to another this may +# raise a RecursionError +# to avoid this, import only the models that you directly need like: +# from from web.src.services.bio_api.openapi.model.pet import Pet +# or import this package, but before doing it, use: +# import sys +# sys.setrecursionlimit(n) + +from web.src.services.bio_api.openapi.model.common_post_response import CommonPOSTResponse +from web.src.services.bio_api.openapi.model.distance_matrix_get_response import DistanceMatrixGETResponse +from web.src.services.bio_api.openapi.model.distance_matrix_request import DistanceMatrixRequest +from web.src.services.bio_api.openapi.model.distance_matrix_result import DistanceMatrixResult +from web.src.services.bio_api.openapi.model.hc_tree_calc_get_response import HCTreeCalcGETResponse +from web.src.services.bio_api.openapi.model.hc_tree_calc_request import HCTreeCalcRequest +from web.src.services.bio_api.openapi.model.http_validation_error import HTTPValidationError +from web.src.services.bio_api.openapi.model.message import Message +from web.src.services.bio_api.openapi.model.nearest_neighbors_get_response import NearestNeighborsGETResponse +from web.src.services.bio_api.openapi.model.nearest_neighbors_request import NearestNeighborsRequest +from web.src.services.bio_api.openapi.model.neighbor import Neighbor +from web.src.services.bio_api.openapi.model.status import Status +from web.src.services.bio_api.openapi.model.validation_error import ValidationError diff --git a/web/src/services/bio_api/openapi/rest.py b/web/src/services/bio_api/openapi/rest.py new file mode 100644 index 00000000..5ded40ca --- /dev/null +++ b/web/src/services/bio_api/openapi/rest.py @@ -0,0 +1,292 @@ +""" + Bio API + + REST API for controlling bioinformatic calculations1 # noqa: E501 + + The version of the OpenAPI document: 0.2.0 + Generated by: https://openapi-generator.tech +""" + + +import io +import json +import logging +import re +import ssl +from urllib.parse import urlencode + +import urllib3 + +from web.src.services.bio_api.openapi.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError + + +logger = logging.getLogger(__name__) + + +class RESTResponse(io.IOBase): + + def __init__(self, resp): + self.urllib3_response = resp + self.status = resp.status + self.reason = resp.reason + self.data = resp.data + + def getheaders(self): + """Returns a dictionary of the response headers.""" + return self.urllib3_response.getheaders() + + def getheader(self, name, default=None): + """Returns a given response header.""" + return self.urllib3_response.getheader(name, default) + + +class RESTClientObject(object): + + def __init__(self, configuration, pools_size=4, maxsize=None): + # urllib3.PoolManager will pass all kw parameters to connectionpool + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 + # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 + # maxsize is the number of requests to host that are allowed in parallel # noqa: E501 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 + + # cert_reqs + if configuration.verify_ssl: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + + addition_pool_args = {} + if configuration.assert_hostname is not None: + addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 + + if configuration.retries is not None: + addition_pool_args['retries'] = configuration.retries + + if configuration.socket_options is not None: + addition_pool_args['socket_options'] = configuration.socket_options + + if maxsize is None: + if configuration.connection_pool_maxsize is not None: + maxsize = configuration.connection_pool_maxsize + else: + maxsize = 4 + + # https pool manager + if configuration.proxy: + self.pool_manager = urllib3.ProxyManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=configuration.ssl_ca_cert, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + proxy_url=configuration.proxy, + proxy_headers=configuration.proxy_headers, + **addition_pool_args + ) + else: + self.pool_manager = urllib3.PoolManager( + num_pools=pools_size, + maxsize=maxsize, + cert_reqs=cert_reqs, + ca_certs=configuration.ssl_ca_cert, + cert_file=configuration.cert_file, + key_file=configuration.key_file, + **addition_pool_args + ) + + def request(self, method, url, query_params=None, headers=None, + body=None, post_params=None, _preload_content=True, + _request_timeout=None): + """Perform requests. + + :param method: http request method + :param url: http request url + :param query_params: query parameters in the url + :param headers: http request headers + :param body: request json body, for `application/json` + :param post_params: request post parameters, + `application/x-www-form-urlencoded` + and `multipart/form-data` + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + """ + method = method.upper() + assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', + 'PATCH', 'OPTIONS'] + + if post_params and body: + raise ApiValueError( + "body parameter cannot be used with post_params parameter." + ) + + post_params = post_params or {} + headers = headers or {} + + timeout = None + if _request_timeout: + if isinstance(_request_timeout, (int, float)): # noqa: E501,F821 + timeout = urllib3.Timeout(total=_request_timeout) + elif (isinstance(_request_timeout, tuple) and + len(_request_timeout) == 2): + timeout = urllib3.Timeout( + connect=_request_timeout[0], read=_request_timeout[1]) + + try: + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` + if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: + # Only set a default Content-Type for POST, PUT, PATCH and OPTIONS requests + if (method != 'DELETE') and ('Content-Type' not in headers): + headers['Content-Type'] = 'application/json' + if query_params: + url += '?' + urlencode(query_params) + if ('Content-Type' not in headers) or (re.search('json', headers['Content-Type'], re.IGNORECASE)): + request_body = None + if body is not None: + request_body = json.dumps(body) + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=False, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + elif headers['Content-Type'] == 'multipart/form-data': + # must del headers['Content-Type'], or the correct + # Content-Type which generated by urllib3 will be + # overwritten. + del headers['Content-Type'] + r = self.pool_manager.request( + method, url, + fields=post_params, + encode_multipart=True, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + # Pass a `string` parameter directly in the body to support + # other content types than Json when `body` argument is + # provided in serialized form + elif isinstance(body, str) or isinstance(body, bytes): + request_body = body + r = self.pool_manager.request( + method, url, + body=request_body, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + else: + # Cannot generate the request from given parameters + msg = """Cannot prepare a request message for provided + arguments. Please check that your arguments match + declared content type.""" + raise ApiException(status=0, reason=msg) + # For `GET`, `HEAD` + else: + r = self.pool_manager.request(method, url, + fields=query_params, + preload_content=_preload_content, + timeout=timeout, + headers=headers) + except urllib3.exceptions.SSLError as e: + msg = "{0}\n{1}".format(type(e).__name__, str(e)) + raise ApiException(status=0, reason=msg) + + if _preload_content: + r = RESTResponse(r) + + # log response body + logger.debug("response body: %s", r.data) + + if not 200 <= r.status <= 299: + if r.status == 401: + raise UnauthorizedException(http_resp=r) + + if r.status == 403: + raise ForbiddenException(http_resp=r) + + if r.status == 404: + raise NotFoundException(http_resp=r) + + if 500 <= r.status <= 599: + raise ServiceException(http_resp=r) + + raise ApiException(http_resp=r) + + return r + + def GET(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("GET", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def HEAD(self, url, headers=None, query_params=None, _preload_content=True, + _request_timeout=None): + return self.request("HEAD", url, + headers=headers, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + query_params=query_params) + + def OPTIONS(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("OPTIONS", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def DELETE(self, url, headers=None, query_params=None, body=None, + _preload_content=True, _request_timeout=None): + return self.request("DELETE", url, + headers=headers, + query_params=query_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def POST(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("POST", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def PUT(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PUT", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) + + def PATCH(self, url, headers=None, query_params=None, post_params=None, + body=None, _preload_content=True, _request_timeout=None): + return self.request("PATCH", url, + headers=headers, + query_params=query_params, + post_params=post_params, + _preload_content=_preload_content, + _request_timeout=_request_timeout, + body=body) diff --git a/web/src/services/bio_api/openapi_README.md b/web/src/services/bio_api/openapi_README.md new file mode 100644 index 00000000..506ac552 --- /dev/null +++ b/web/src/services/bio_api/openapi_README.md @@ -0,0 +1,126 @@ +# web.src.services.bio-api.openapi +REST API for controlling bioinformatic calculations1 + +The `web.src.services.bio_api.openapi` package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: + +- API version: 0.2.0 +- Package version: 1.0.0 +- Build package: org.openapitools.codegen.languages.PythonClientCodegen + +## Requirements. + +Python >= 3.6 + +## Installation & Usage + +This python library package is generated without supporting files like setup.py or requirements files + +To be able to use it, you will need these dependencies in your own package that uses this library: + +* urllib3 >= 1.25.3 +* python-dateutil + +## Getting Started + +In your own code, to use this library to connect and interact with web.src.services.bio-api.openapi, +you can run the following: + +```python + +import time +import web.src.services.bio_api.openapi +from pprint import pprint +from web.src.services.bio_api.openapi.api import distances_api +from web.src.services.bio_api.openapi.model.common_post_response import CommonPOSTResponse +from web.src.services.bio_api.openapi.model.distance_matrix_get_response import DistanceMatrixGETResponse +from web.src.services.bio_api.openapi.model.distance_matrix_request import DistanceMatrixRequest +from web.src.services.bio_api.openapi.model.http_validation_error import HTTPValidationError +from web.src.services.bio_api.openapi.model.message import Message +# Defining the host is optional and defaults to http://localhost:8000 +# See configuration.py for a list of all supported configuration parameters. +configuration = web.src.services.bio_api.openapi.Configuration( + host = "http://localhost:8000" +) + + + +# Enter a context with an instance of the API client +with web.src.services.bio_api.openapi.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = distances_api.DistancesApi(api_client) + distance_matrix_request = DistanceMatrixRequest( + seq_collection="seq_collection_example", + seqid_field_path="seqid_field_path_example", + profile_field_path="profile_field_path_example", + seq_mongo_ids=[ + None, + ], + ) # DistanceMatrixRequest | + + try: + # Dmx From Mongodb + api_response = api_instance.dmx_from_mongodb_v1_distance_calculations_post(distance_matrix_request) + pprint(api_response) + except web.src.services.bio_api.openapi.ApiException as e: + print("Exception when calling DistancesApi->dmx_from_mongodb_v1_distance_calculations_post: %s\n" % e) +``` + +## Documentation for API Endpoints + +All URIs are relative to *http://localhost:8000* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*DistancesApi* | [**dmx_from_mongodb_v1_distance_calculations_post**](web/src/services/bio_api/openapi/docs/DistancesApi.md#dmx_from_mongodb_v1_distance_calculations_post) | **POST** /v1/distance_calculations | Dmx From Mongodb +*DistancesApi* | [**dmx_result_v1_distance_calculations_dc_id_get**](web/src/services/bio_api/openapi/docs/DistancesApi.md#dmx_result_v1_distance_calculations_dc_id_get) | **GET** /v1/distance_calculations/{dc_id} | Dmx Result +*NearestNeighborsApi* | [**nearest_neighbors_v1_nearest_neighbors_post**](web/src/services/bio_api/openapi/docs/NearestNeighborsApi.md#nearest_neighbors_v1_nearest_neighbors_post) | **POST** /v1/nearest_neighbors | Nearest Neighbors +*NearestNeighborsApi* | [**nn_result_v1_nearest_neighbors_nn_id_get**](web/src/services/bio_api/openapi/docs/NearestNeighborsApi.md#nn_result_v1_nearest_neighbors_nn_id_get) | **GET** /v1/nearest_neighbors/{nn_id} | Nn Result +*TreesApi* | [**hc_tree_from_dmx_job_v1_trees_post**](web/src/services/bio_api/openapi/docs/TreesApi.md#hc_tree_from_dmx_job_v1_trees_post) | **POST** /v1/trees | Hc Tree From Dmx Job +*TreesApi* | [**hc_tree_result_v1_trees_tc_id_get**](web/src/services/bio_api/openapi/docs/TreesApi.md#hc_tree_result_v1_trees_tc_id_get) | **GET** /v1/trees/{tc_id} | Hc Tree Result + + +## Documentation For Models + + - [CommonPOSTResponse](web/src/services/bio_api/openapi/docs/CommonPOSTResponse.md) + - [DistanceMatrixGETResponse](web/src/services/bio_api/openapi/docs/DistanceMatrixGETResponse.md) + - [DistanceMatrixRequest](web/src/services/bio_api/openapi/docs/DistanceMatrixRequest.md) + - [DistanceMatrixResult](web/src/services/bio_api/openapi/docs/DistanceMatrixResult.md) + - [HCTreeCalcGETResponse](web/src/services/bio_api/openapi/docs/HCTreeCalcGETResponse.md) + - [HCTreeCalcRequest](web/src/services/bio_api/openapi/docs/HCTreeCalcRequest.md) + - [HTTPValidationError](web/src/services/bio_api/openapi/docs/HTTPValidationError.md) + - [Message](web/src/services/bio_api/openapi/docs/Message.md) + - [NearestNeighborsGETResponse](web/src/services/bio_api/openapi/docs/NearestNeighborsGETResponse.md) + - [NearestNeighborsRequest](web/src/services/bio_api/openapi/docs/NearestNeighborsRequest.md) + - [Neighbor](web/src/services/bio_api/openapi/docs/Neighbor.md) + - [Status](web/src/services/bio_api/openapi/docs/Status.md) + - [ValidationError](web/src/services/bio_api/openapi/docs/ValidationError.md) + + +## Documentation For Authorization + + All endpoints do not require authorization. + +## Author + + + + +## Notes for Large OpenAPI documents +If the OpenAPI document is large, imports in web.src.services.bio_api.openapi.apis and web.src.services.bio_api.openapi.models may fail with a +RecursionError indicating the maximum recursion limit has been exceeded. In that case, there are a couple of solutions: + +Solution 1: +Use specific imports for apis and models like: +- `from web.src.services.bio_api.openapi.api.default_api import DefaultApi` +- `from web.src.services.bio_api.openapi.model.pet import Pet` + +Solution 2: +Before importing the package, adjust the maximum recursion limit as shown below: +``` +import sys +sys.setrecursionlimit(1500) +import web.src.services.bio_api.openapi +from web.src.services.bio_api.openapi.apis import * +from web.src.services.bio_api.openapi.models import * +``` + From bdaa6121f6d9f8053599cc79106c637fe50b2e26 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 07:09:53 +0200 Subject: [PATCH 03/18] chore: update bio_api spec --- openapi_specs/bio_api.yaml | 10 ++++------ web/openapi_specs/bio_api.yaml | 12 +++++------- .../controllers/NearestNeighborsController.py | 3 +-- web/src/services/bio_api/openapi/__init__.py | 2 +- .../bio_api/openapi/api/distances_api.py | 2 +- .../bio_api/openapi/api/nearest_neighbors_api.py | 2 +- .../services/bio_api/openapi/api/trees_api.py | 2 +- web/src/services/bio_api/openapi/api_client.py | 2 +- .../services/bio_api/openapi/configuration.py | 2 +- web/src/services/bio_api/openapi/exceptions.py | 2 +- .../openapi/model/common_post_response.py | 2 +- .../model/distance_matrix_get_response.py | 2 +- .../openapi/model/distance_matrix_request.py | 2 +- .../openapi/model/distance_matrix_result.py | 2 +- .../openapi/model/hc_tree_calc_get_response.py | 2 +- .../openapi/model/hc_tree_calc_request.py | 2 +- .../openapi/model/http_validation_error.py | 2 +- .../services/bio_api/openapi/model/message.py | 2 +- .../model/nearest_neighbors_get_response.py | 16 +++++++++------- .../openapi/model/nearest_neighbors_request.py | 16 +++++++++------- .../services/bio_api/openapi/model/neighbor.py | 2 +- web/src/services/bio_api/openapi/model/status.py | 2 +- .../bio_api/openapi/model/validation_error.py | 2 +- web/src/services/bio_api/openapi/model_utils.py | 2 +- web/src/services/bio_api/openapi/rest.py | 2 +- web/src/services/bio_api/openapi_README.md | 2 +- 26 files changed, 49 insertions(+), 50 deletions(-) diff --git a/openapi_specs/bio_api.yaml b/openapi_specs/bio_api.yaml index 4cc28d01..480ff2aa 100644 --- a/openapi_specs/bio_api.yaml +++ b/openapi_specs/bio_api.yaml @@ -465,9 +465,7 @@ components: type: string title: Seq Collection filtering: - anyOf: - - type: object - - type: 'null' + type: object title: Filtering profile_field_path: type: string @@ -495,6 +493,7 @@ components: - status - finished_at - seq_collection + - filtering - profile_field_path - input_mongo_id - cutoff @@ -507,9 +506,7 @@ components: type: string title: Seq Collection filtering: - anyOf: - - type: object - - type: 'null' + type: object title: Filtering profile_field_path: type: string @@ -526,6 +523,7 @@ components: type: object required: - seq_collection + - filtering - profile_field_path - input_mongo_id - cutoff diff --git a/web/openapi_specs/bio_api.yaml b/web/openapi_specs/bio_api.yaml index 11e58376..480ff2aa 100644 --- a/web/openapi_specs/bio_api.yaml +++ b/web/openapi_specs/bio_api.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Bio API - description: REST API for controlling bioinformatic calculations1 + description: REST API for controlling bioinformatic calculations version: 0.2.0 servers: - url: 'http://localhost:8000/' @@ -465,9 +465,7 @@ components: type: string title: Seq Collection filtering: - anyOf: - - type: object - - type: 'null' + type: object title: Filtering profile_field_path: type: string @@ -495,6 +493,7 @@ components: - status - finished_at - seq_collection + - filtering - profile_field_path - input_mongo_id - cutoff @@ -507,9 +506,7 @@ components: type: string title: Seq Collection filtering: - anyOf: - - type: object - - type: 'null' + type: object title: Filtering profile_field_path: type: string @@ -526,6 +523,7 @@ components: type: object required: - seq_collection + - filtering - profile_field_path - input_mongo_id - cutoff diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 4ecddbec..7e18a47c 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -10,8 +10,7 @@ def post(user, token, body: NearestNeighborsRequest): with ApiClient(Configuration(host="http://bio_api:8000")) as api_client: api_instance = NearestNeighborsApi(api_client) - # TODO: filtering={"species_final": "value"} - request = BioNearestNeighborsRequest("sap_analysis_results", "st_alleles", body.id, body.cutoff, False) + request = BioNearestNeighborsRequest("sap_analysis_results", {"species_final": "value"}, "st_alleles", body.id, body.cutoff, False) api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) return jsonify(api_response.to_dict()) diff --git a/web/src/services/bio_api/openapi/__init__.py b/web/src/services/bio_api/openapi/__init__.py index 77999a41..93482465 100644 --- a/web/src/services/bio_api/openapi/__init__.py +++ b/web/src/services/bio_api/openapi/__init__.py @@ -3,7 +3,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/api/distances_api.py b/web/src/services/bio_api/openapi/api/distances_api.py index 74716757..cb202bfd 100644 --- a/web/src/services/bio_api/openapi/api/distances_api.py +++ b/web/src/services/bio_api/openapi/api/distances_api.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py b/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py index 40710cac..901a8955 100644 --- a/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py +++ b/web/src/services/bio_api/openapi/api/nearest_neighbors_api.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/api/trees_api.py b/web/src/services/bio_api/openapi/api/trees_api.py index 427096a5..457db7b6 100644 --- a/web/src/services/bio_api/openapi/api/trees_api.py +++ b/web/src/services/bio_api/openapi/api/trees_api.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/api_client.py b/web/src/services/bio_api/openapi/api_client.py index 4fa4dbc2..fb471167 100644 --- a/web/src/services/bio_api/openapi/api_client.py +++ b/web/src/services/bio_api/openapi/api_client.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/configuration.py b/web/src/services/bio_api/openapi/configuration.py index a8f90aa5..fa99c49f 100644 --- a/web/src/services/bio_api/openapi/configuration.py +++ b/web/src/services/bio_api/openapi/configuration.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/exceptions.py b/web/src/services/bio_api/openapi/exceptions.py index ab6afaf1..765a9e56 100644 --- a/web/src/services/bio_api/openapi/exceptions.py +++ b/web/src/services/bio_api/openapi/exceptions.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/common_post_response.py b/web/src/services/bio_api/openapi/model/common_post_response.py index d1ed9d84..7fc3ce19 100644 --- a/web/src/services/bio_api/openapi/model/common_post_response.py +++ b/web/src/services/bio_api/openapi/model/common_post_response.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py b/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py index 10094a5a..035acecb 100644 --- a/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py +++ b/web/src/services/bio_api/openapi/model/distance_matrix_get_response.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/distance_matrix_request.py b/web/src/services/bio_api/openapi/model/distance_matrix_request.py index 8ed97b5c..89b9b3ed 100644 --- a/web/src/services/bio_api/openapi/model/distance_matrix_request.py +++ b/web/src/services/bio_api/openapi/model/distance_matrix_request.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/distance_matrix_result.py b/web/src/services/bio_api/openapi/model/distance_matrix_result.py index 9c5c37f5..7feca567 100644 --- a/web/src/services/bio_api/openapi/model/distance_matrix_result.py +++ b/web/src/services/bio_api/openapi/model/distance_matrix_result.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py b/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py index bbfedee0..bc11dffb 100644 --- a/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py +++ b/web/src/services/bio_api/openapi/model/hc_tree_calc_get_response.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py b/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py index cbea077d..8e2b34eb 100644 --- a/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py +++ b/web/src/services/bio_api/openapi/model/hc_tree_calc_request.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/http_validation_error.py b/web/src/services/bio_api/openapi/model/http_validation_error.py index 3a70e4ac..c99fab43 100644 --- a/web/src/services/bio_api/openapi/model/http_validation_error.py +++ b/web/src/services/bio_api/openapi/model/http_validation_error.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/message.py b/web/src/services/bio_api/openapi/model/message.py index 15ead9e4..56e8bcb9 100644 --- a/web/src/services/bio_api/openapi/model/message.py +++ b/web/src/services/bio_api/openapi/model/message.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py b/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py index af1766d4..ef764f7a 100644 --- a/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py +++ b/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech @@ -92,12 +92,12 @@ def openapi_types(): 'status': (Status,), # noqa: E501 'finished_at': (dict,), # noqa: E501 'seq_collection': (str,), # noqa: E501 + 'filtering': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), # noqa: E501 'profile_field_path': (str,), # noqa: E501 'input_mongo_id': (str,), # noqa: E501 'cutoff': (int,), # noqa: E501 'unknowns_are_diffs': (bool,), # noqa: E501 'result': (dict,), # noqa: E501 - 'filtering': (dict,), # noqa: E501 } @cached_property @@ -111,12 +111,12 @@ def discriminator(): 'status': 'status', # noqa: E501 'finished_at': 'finished_at', # noqa: E501 'seq_collection': 'seq_collection', # noqa: E501 + 'filtering': 'filtering', # noqa: E501 'profile_field_path': 'profile_field_path', # noqa: E501 'input_mongo_id': 'input_mongo_id', # noqa: E501 'cutoff': 'cutoff', # noqa: E501 'unknowns_are_diffs': 'unknowns_are_diffs', # noqa: E501 'result': 'result', # noqa: E501 - 'filtering': 'filtering', # noqa: E501 } read_only_vars = { @@ -126,7 +126,7 @@ def discriminator(): @classmethod @convert_js_args_to_python_args - def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, result, *args, **kwargs): # noqa: E501 + def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collection, filtering, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, result, *args, **kwargs): # noqa: E501 """NearestNeighborsGETResponse - a model defined in OpenAPI Args: @@ -135,6 +135,7 @@ def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collect status (Status): finished_at (dict): seq_collection (str): + filtering ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): profile_field_path (str): input_mongo_id (str): cutoff (int): @@ -172,7 +173,6 @@ def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collect Animal class but this time we won't travel through its discriminator because we passed in _visited_composed_classes = (Animal,) - filtering (dict): [optional] # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) @@ -205,6 +205,7 @@ def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collect self.status = status self.finished_at = finished_at self.seq_collection = seq_collection + self.filtering = filtering self.profile_field_path = profile_field_path self.input_mongo_id = input_mongo_id self.cutoff = cutoff @@ -230,7 +231,7 @@ def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collect ]) @convert_js_args_to_python_args - def __init__(self, job_id, created_at, status, finished_at, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, result, *args, **kwargs): # noqa: E501 + def __init__(self, job_id, created_at, status, finished_at, seq_collection, filtering, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, result, *args, **kwargs): # noqa: E501 """NearestNeighborsGETResponse - a model defined in OpenAPI Args: @@ -239,6 +240,7 @@ def __init__(self, job_id, created_at, status, finished_at, seq_collection, prof status (Status): finished_at (dict): seq_collection (str): + filtering ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): profile_field_path (str): input_mongo_id (str): cutoff (int): @@ -276,7 +278,6 @@ def __init__(self, job_id, created_at, status, finished_at, seq_collection, prof Animal class but this time we won't travel through its discriminator because we passed in _visited_composed_classes = (Animal,) - filtering (dict): [optional] # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) @@ -307,6 +308,7 @@ def __init__(self, job_id, created_at, status, finished_at, seq_collection, prof self.status = status self.finished_at = finished_at self.seq_collection = seq_collection + self.filtering = filtering self.profile_field_path = profile_field_path self.input_mongo_id = input_mongo_id self.cutoff = cutoff diff --git a/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py b/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py index d821a90d..2760566b 100644 --- a/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py +++ b/web/src/services/bio_api/openapi/model/nearest_neighbors_request.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech @@ -82,11 +82,11 @@ def openapi_types(): """ return { 'seq_collection': (str,), # noqa: E501 + 'filtering': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), # noqa: E501 'profile_field_path': (str,), # noqa: E501 'input_mongo_id': (str,), # noqa: E501 'cutoff': (int,), # noqa: E501 'unknowns_are_diffs': (bool,), # noqa: E501 - 'filtering': (dict,), # noqa: E501 } @cached_property @@ -96,11 +96,11 @@ def discriminator(): attribute_map = { 'seq_collection': 'seq_collection', # noqa: E501 + 'filtering': 'filtering', # noqa: E501 'profile_field_path': 'profile_field_path', # noqa: E501 'input_mongo_id': 'input_mongo_id', # noqa: E501 'cutoff': 'cutoff', # noqa: E501 'unknowns_are_diffs': 'unknowns_are_diffs', # noqa: E501 - 'filtering': 'filtering', # noqa: E501 } read_only_vars = { @@ -110,11 +110,12 @@ def discriminator(): @classmethod @convert_js_args_to_python_args - def _from_openapi_data(cls, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, *args, **kwargs): # noqa: E501 + def _from_openapi_data(cls, seq_collection, filtering, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, *args, **kwargs): # noqa: E501 """NearestNeighborsRequest - a model defined in OpenAPI Args: seq_collection (str): + filtering ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): profile_field_path (str): input_mongo_id (str): cutoff (int): @@ -151,7 +152,6 @@ def _from_openapi_data(cls, seq_collection, profile_field_path, input_mongo_id, Animal class but this time we won't travel through its discriminator because we passed in _visited_composed_classes = (Animal,) - filtering (dict): [optional] # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) @@ -180,6 +180,7 @@ def _from_openapi_data(cls, seq_collection, profile_field_path, input_mongo_id, self._visited_composed_classes = _visited_composed_classes + (self.__class__,) self.seq_collection = seq_collection + self.filtering = filtering self.profile_field_path = profile_field_path self.input_mongo_id = input_mongo_id self.cutoff = cutoff @@ -204,11 +205,12 @@ def _from_openapi_data(cls, seq_collection, profile_field_path, input_mongo_id, ]) @convert_js_args_to_python_args - def __init__(self, seq_collection, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, *args, **kwargs): # noqa: E501 + def __init__(self, seq_collection, filtering, profile_field_path, input_mongo_id, cutoff, unknowns_are_diffs, *args, **kwargs): # noqa: E501 """NearestNeighborsRequest - a model defined in OpenAPI Args: seq_collection (str): + filtering ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): profile_field_path (str): input_mongo_id (str): cutoff (int): @@ -245,7 +247,6 @@ def __init__(self, seq_collection, profile_field_path, input_mongo_id, cutoff, u Animal class but this time we won't travel through its discriminator because we passed in _visited_composed_classes = (Animal,) - filtering (dict): [optional] # noqa: E501 """ _check_type = kwargs.pop('_check_type', True) @@ -272,6 +273,7 @@ def __init__(self, seq_collection, profile_field_path, input_mongo_id, cutoff, u self._visited_composed_classes = _visited_composed_classes + (self.__class__,) self.seq_collection = seq_collection + self.filtering = filtering self.profile_field_path = profile_field_path self.input_mongo_id = input_mongo_id self.cutoff = cutoff diff --git a/web/src/services/bio_api/openapi/model/neighbor.py b/web/src/services/bio_api/openapi/model/neighbor.py index a8583ccd..175e0618 100644 --- a/web/src/services/bio_api/openapi/model/neighbor.py +++ b/web/src/services/bio_api/openapi/model/neighbor.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/status.py b/web/src/services/bio_api/openapi/model/status.py index 70965497..3d05c2e5 100644 --- a/web/src/services/bio_api/openapi/model/status.py +++ b/web/src/services/bio_api/openapi/model/status.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model/validation_error.py b/web/src/services/bio_api/openapi/model/validation_error.py index 631aa915..4bbf8f98 100644 --- a/web/src/services/bio_api/openapi/model/validation_error.py +++ b/web/src/services/bio_api/openapi/model/validation_error.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/model_utils.py b/web/src/services/bio_api/openapi/model_utils.py index 27c90d0d..9f5ddfd3 100644 --- a/web/src/services/bio_api/openapi/model_utils.py +++ b/web/src/services/bio_api/openapi/model_utils.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi/rest.py b/web/src/services/bio_api/openapi/rest.py index 5ded40ca..36276082 100644 --- a/web/src/services/bio_api/openapi/rest.py +++ b/web/src/services/bio_api/openapi/rest.py @@ -1,7 +1,7 @@ """ Bio API - REST API for controlling bioinformatic calculations1 # noqa: E501 + REST API for controlling bioinformatic calculations # noqa: E501 The version of the OpenAPI document: 0.2.0 Generated by: https://openapi-generator.tech diff --git a/web/src/services/bio_api/openapi_README.md b/web/src/services/bio_api/openapi_README.md index 506ac552..218a6eaf 100644 --- a/web/src/services/bio_api/openapi_README.md +++ b/web/src/services/bio_api/openapi_README.md @@ -1,5 +1,5 @@ # web.src.services.bio-api.openapi -REST API for controlling bioinformatic calculations1 +REST API for controlling bioinformatic calculations The `web.src.services.bio_api.openapi` package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: From f6f4a8f0b02ed9dcbdb018f9931c7473603ccacf Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 08:08:31 +0200 Subject: [PATCH 04/18] feat: set species filter --- .../analysis/nearest-neighbor/nearest-neighbor-modal.tsx | 3 ++- web/src/SAP/src/controllers/NearestNeighborsController.py | 7 +++++-- web/src/SAP/src/repositories/analysis.py | 7 ++++++- web/src/SAP/src/repositories/samples.py | 7 +++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index 3d782ff3..d4256e3a 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -40,9 +40,10 @@ export const NearestNeighborModal = (props: Props) => { useEffect(() => { // TODO + const first = Object.values(selection)[0]; dispatch( requestAsync({ - ...nearestNeighborsRequest({ id: "6211e8bc207a1f796ec0b69f", cutoff: 15 }), + ...nearestNeighborsRequest({ id: first.original.id, cutoff }), }) ); }, [dispatch]); diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 7e18a47c..34ae4ff1 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -1,4 +1,5 @@ from web.src.SAP.generated.models.nearest_neighbors_request import NearestNeighborsRequest +from web.src.SAP.src.repositories.analysis import get_single_analysis_by_object_id from web.src.services.bio_api.openapi.api.nearest_neighbors_api import NearestNeighborsApi from web.src.services.bio_api.openapi.api_client import ApiClient from web.src.services.bio_api.openapi.configuration import Configuration @@ -8,9 +9,11 @@ def post(user, token, body: NearestNeighborsRequest): with ApiClient(Configuration(host="http://bio_api:8000")) as api_client: - api_instance = NearestNeighborsApi(api_client) + analysis = get_single_analysis_by_object_id(body.id) - request = BioNearestNeighborsRequest("sap_analysis_results", {"species_final": "value"}, "st_alleles", body.id, body.cutoff, False) + api_instance = NearestNeighborsApi(api_client) + filtering = {"categories.species_detection.summary.detected_species": analysis["species_final"]} + request = BioNearestNeighborsRequest("samples", filtering, "categories.cgmlst.report.alleles", body.id, body.cutoff, False) api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) return jsonify(api_response.to_dict()) diff --git a/web/src/SAP/src/repositories/analysis.py b/web/src/SAP/src/repositories/analysis.py index 87cde7c6..5c1061d2 100644 --- a/web/src/SAP/src/repositories/analysis.py +++ b/web/src/SAP/src/repositories/analysis.py @@ -15,7 +15,7 @@ ANALYSIS_COL_NAME, ) import sys - +from bson.objectid import ObjectId def remove_id(item): item.pop("_id", None) @@ -116,6 +116,11 @@ def get_single_analysis(identifier: str) -> Dict[str, Any]: samples = mydb[ANALYSIS_COL_NAME] return samples.find_one({"sequence_id": f"{identifier}"}, {"_id": 0}) +def get_single_analysis_by_object_id(id: str) -> Dict[str, Any]: + conn = get_connection() + mydb = conn[DB_NAME] + analysis = mydb[ANALYSIS_COL_NAME] + return analysis.find_one(ObjectId(id)) def get_analysis_with_metadata(sequence_id: str) -> Dict[str, Any]: conn = get_connection() diff --git a/web/src/SAP/src/repositories/samples.py b/web/src/SAP/src/repositories/samples.py index 217c1453..4c81e215 100644 --- a/web/src/SAP/src/repositories/samples.py +++ b/web/src/SAP/src/repositories/samples.py @@ -4,9 +4,16 @@ DB_NAME, SAMPLES_COL_NAME, ) +from bson.objectid import ObjectId def get_single_sample(sequence_id: str) -> Dict[str, Any]: conn = get_connection() mydb = conn[DB_NAME] samples = mydb[SAMPLES_COL_NAME] return samples.find_one({"categories.sample_info.summary.sofi_sequence_id": f"{sequence_id}"}, {"_id": 0}) + +def get_single_sample_by_object_id(id: str) -> Dict[str, Any]: + conn = get_connection() + mydb = conn[DB_NAME] + samples = mydb[SAMPLES_COL_NAME] + return samples.find_one(ObjectId(id)) From 9f7a5d396eb19cd067ea369b5accedfc1c4f885f Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 08:21:02 +0200 Subject: [PATCH 05/18] feat: added unknownsAreDiffs to SOFI nn request --- .../nearest-neighbor-modal.tsx | 9 ++++-- .../models/NearestNeighborsRequest.ts | 8 +++++ openapi_specs/SOFI/SOFI.yaml | 4 +++ web/openapi_specs/SOFI/SOFI.yaml | 4 +++ .../models/nearest_neighbors_request.py | 30 ++++++++++++++++++- web/src/SAP/generated/openapi/openapi.yaml | 5 ++++ .../test/test_nearest_neighbors_controller.py | 1 + .../controllers/NearestNeighborsController.py | 2 +- 8 files changed, 58 insertions(+), 5 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index d4256e3a..055cd2c0 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -43,10 +43,13 @@ export const NearestNeighborModal = (props: Props) => { const first = Object.values(selection)[0]; dispatch( requestAsync({ - ...nearestNeighborsRequest({ id: first.original.id, cutoff }), + ...nearestNeighborsRequest({ + id: first.original.id, + cutoff, + unknownsAreDiffs: true }), }) ); - }, [dispatch]); + }, [dispatch, selection, cutoff]); const onSearch = useCallback(() => { setIsSearching(true); @@ -81,8 +84,8 @@ export const NearestNeighborModal = (props: Props) => { - // TODO: unknowns_are_diffs ) : ( + // TODO: unknowns_are_diffs )} diff --git a/app/src/sap-client/models/NearestNeighborsRequest.ts b/app/src/sap-client/models/NearestNeighborsRequest.ts index ce2d41b4..e790cd65 100644 --- a/app/src/sap-client/models/NearestNeighborsRequest.ts +++ b/app/src/sap-client/models/NearestNeighborsRequest.ts @@ -30,12 +30,19 @@ export interface NearestNeighborsRequest { * @memberof NearestNeighborsRequest */ cutoff: number; + /** + * + * @type {boolean} + * @memberof NearestNeighborsRequest + */ + unknownsAreDiffs: boolean; } export function NearestNeighborsRequestFromJSON(json: any): NearestNeighborsRequest { return { 'id': json['id'], 'cutoff': json['cutoff'], + 'unknownsAreDiffs': json['unknownsAreDiffs'], }; } @@ -46,6 +53,7 @@ export function NearestNeighborsRequestToJSON(value?: NearestNeighborsRequest): return { 'id': value.id, 'cutoff': value.cutoff, + 'unknownsAreDiffs': value.unknownsAreDiffs, }; } diff --git a/openapi_specs/SOFI/SOFI.yaml b/openapi_specs/SOFI/SOFI.yaml index 9a0484e3..55a89ce5 100644 --- a/openapi_specs/SOFI/SOFI.yaml +++ b/openapi_specs/SOFI/SOFI.yaml @@ -1304,10 +1304,14 @@ components: cutoff: type: integer title: Cutoff + unknownsAreDiffs: + type: boolean + title: Unknowns Are Diffs type: object required: - id - cutoff + - unknownsAreDiffs title: NearestNeighborsRequest description: Parameters for a REST request for a nearest neighbors calculation. diff --git a/web/openapi_specs/SOFI/SOFI.yaml b/web/openapi_specs/SOFI/SOFI.yaml index 9a0484e3..55a89ce5 100644 --- a/web/openapi_specs/SOFI/SOFI.yaml +++ b/web/openapi_specs/SOFI/SOFI.yaml @@ -1304,10 +1304,14 @@ components: cutoff: type: integer title: Cutoff + unknownsAreDiffs: + type: boolean + title: Unknowns Are Diffs type: object required: - id - cutoff + - unknownsAreDiffs title: NearestNeighborsRequest description: Parameters for a REST request for a nearest neighbors calculation. diff --git a/web/src/SAP/generated/models/nearest_neighbors_request.py b/web/src/SAP/generated/models/nearest_neighbors_request.py index 96fa4a09..40da8253 100644 --- a/web/src/SAP/generated/models/nearest_neighbors_request.py +++ b/web/src/SAP/generated/models/nearest_neighbors_request.py @@ -17,26 +17,31 @@ class NearestNeighborsRequest(Model): Do not edit the class manually. """ - def __init__(self, id=None, cutoff=None): # noqa: E501 + def __init__(self, id=None, cutoff=None, unknowns_are_diffs=None): # noqa: E501 """NearestNeighborsRequest - a model defined in OpenAPI :param id: The id of this NearestNeighborsRequest. # noqa: E501 :type id: str :param cutoff: The cutoff of this NearestNeighborsRequest. # noqa: E501 :type cutoff: int + :param unknowns_are_diffs: The unknowns_are_diffs of this NearestNeighborsRequest. # noqa: E501 + :type unknowns_are_diffs: bool """ self.openapi_types = { 'id': str, 'cutoff': int, + 'unknowns_are_diffs': bool, } self.attribute_map = { 'id': 'id', 'cutoff': 'cutoff', + 'unknowns_are_diffs': 'unknownsAreDiffs', } self._id = id self._cutoff = cutoff + self._unknowns_are_diffs = unknowns_are_diffs @classmethod def from_dict(cls, dikt): @@ -94,3 +99,26 @@ def cutoff(self, cutoff): raise ValueError("Invalid value for `cutoff`, must not be `None`") # noqa: E501 self._cutoff = cutoff + + @property + def unknowns_are_diffs(self): + """Gets the unknowns_are_diffs of this NearestNeighborsRequest. + + + :return: The unknowns_are_diffs of this NearestNeighborsRequest. + :rtype: bool + """ + return self._unknowns_are_diffs + + @unknowns_are_diffs.setter + def unknowns_are_diffs(self, unknowns_are_diffs): + """Sets the unknowns_are_diffs of this NearestNeighborsRequest. + + + :param unknowns_are_diffs: The unknowns_are_diffs of this NearestNeighborsRequest. + :type unknowns_are_diffs: bool + """ + if unknowns_are_diffs is None: + raise ValueError("Invalid value for `unknowns_are_diffs`, must not be `None`") # noqa: E501 + + self._unknowns_are_diffs = unknowns_are_diffs diff --git a/web/src/SAP/generated/openapi/openapi.yaml b/web/src/SAP/generated/openapi/openapi.yaml index e626efd0..8c98136f 100644 --- a/web/src/SAP/generated/openapi/openapi.yaml +++ b/web/src/SAP/generated/openapi/openapi.yaml @@ -1269,6 +1269,7 @@ components: NearestNeighborsRequest: description: Parameters for a REST request for a nearest neighbors calculation. example: + unknownsAreDiffs: true id: id cutoff: 0 properties: @@ -1278,9 +1279,13 @@ components: cutoff: title: Cutoff type: integer + unknownsAreDiffs: + title: Unknowns Are Diffs + type: boolean required: - cutoff - id + - unknownsAreDiffs title: NearestNeighborsRequest type: object BioApiJobResponse: diff --git a/web/src/SAP/generated/test/test_nearest_neighbors_controller.py b/web/src/SAP/generated/test/test_nearest_neighbors_controller.py index 869e9b77..fef78cd3 100644 --- a/web/src/SAP/generated/test/test_nearest_neighbors_controller.py +++ b/web/src/SAP/generated/test/test_nearest_neighbors_controller.py @@ -19,6 +19,7 @@ def test_post(self): """ body = { + "unknownsAreDiffs" : true, "id" : "id", "cutoff" : 0 } diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 34ae4ff1..49b8566c 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -13,7 +13,7 @@ def post(user, token, body: NearestNeighborsRequest): api_instance = NearestNeighborsApi(api_client) filtering = {"categories.species_detection.summary.detected_species": analysis["species_final"]} - request = BioNearestNeighborsRequest("samples", filtering, "categories.cgmlst.report.alleles", body.id, body.cutoff, False) + request = BioNearestNeighborsRequest("samples", filtering, "categories.cgmlst.report.alleles", body.id, body.cutoff, body.unknowns_are_diffs) api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) return jsonify(api_response.to_dict()) From e965ad89a540a51668ea1fa09bfea82b5c611cdd Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 08:40:26 +0200 Subject: [PATCH 06/18] feat: added "unknowns are diffs" to nn ui --- .../nearest-neighbor-modal.tsx | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index 055cd2c0..271e7e60 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -23,6 +23,7 @@ import { useTranslation } from "react-i18next"; import { useDispatch } from "react-redux"; import { requestAsync } from "redux-query"; import { nearestNeighborsRequest } from "./nearest-neighbor-query-configs"; +import { Checkbox } from "@chakra-ui/react"; type Props = { selection: DataTableSelection; @@ -34,7 +35,8 @@ export const NearestNeighborModal = (props: Props) => { const { selection, onClose } = props; const [isSearching, setIsSearching] = useState(false); const [cutoff, setCutoff] = React.useState(15); - const onChangeCutoff = (value) => setCutoff(value); + const onChangeCutoff = (value: string) => setCutoff(parseInt(value)); + const [unknownsAreDiffs, setUnknownsAreDiffs] = useState(true); const dispatch = useDispatch(); @@ -43,13 +45,14 @@ export const NearestNeighborModal = (props: Props) => { const first = Object.values(selection)[0]; dispatch( requestAsync({ - ...nearestNeighborsRequest({ - id: first.original.id, - cutoff, - unknownsAreDiffs: true }), + ...nearestNeighborsRequest({ + id: first.original.id, + cutoff, + unknownsAreDiffs, + }), }) ); - }, [dispatch, selection, cutoff]); + }, [dispatch, selection, cutoff, unknownsAreDiffs]); const onSearch = useCallback(() => { setIsSearching(true); @@ -69,21 +72,33 @@ export const NearestNeighborModal = (props: Props) => { {!isSearching ? ( - <> - Cutoff: - - - - - - - - +
+
+ Cutoff: + + + + + + + +
+
+ setUnknownsAreDiffs(e.target.checked)} + isChecked={unknownsAreDiffs} + > + Unknowns are diffs + +
+
) : ( // TODO: unknowns_are_diffs From 4db50c25d23a8c7ce10c51349058cd8c35005d75 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 09:36:59 +0200 Subject: [PATCH 07/18] fix: sofi bio job response properties casing --- .../nearest-neighbor/nearest-neighbor-modal.tsx | 17 ++++------------- app/src/sap-client/models/BioApiJobResponse.ts | 12 ++++++------ openapi_specs/SOFI/SOFI.yaml | 4 ++-- web/openapi_specs/SOFI/SOFI.yaml | 4 ++-- .../generated/models/bio_api_job_response.py | 4 ++-- web/src/SAP/generated/openapi/openapi.yaml | 8 ++++---- .../controllers/NearestNeighborsController.py | 4 +++- 7 files changed, 23 insertions(+), 30 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index 271e7e60..eb4bae89 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -40,8 +40,9 @@ export const NearestNeighborModal = (props: Props) => { const dispatch = useDispatch(); - useEffect(() => { - // TODO + const onSearch = useCallback(() => { + setIsSearching(true); + const first = Object.values(selection)[0]; dispatch( requestAsync({ @@ -52,17 +53,7 @@ export const NearestNeighborModal = (props: Props) => { }), }) ); - }, [dispatch, selection, cutoff, unknownsAreDiffs]); - - const onSearch = useCallback(() => { - setIsSearching(true); - alert( - `Search, cutoff ${cutoff}, ids ` + - Object.values(selection) - .map((s) => s.original.id) - .join(", ") - ); - }, [setIsSearching, selection, cutoff]); + }, [setIsSearching, selection, cutoff, unknownsAreDiffs]); return ( diff --git a/app/src/sap-client/models/BioApiJobResponse.ts b/app/src/sap-client/models/BioApiJobResponse.ts index 261dd54e..a54e6ece 100644 --- a/app/src/sap-client/models/BioApiJobResponse.ts +++ b/app/src/sap-client/models/BioApiJobResponse.ts @@ -29,13 +29,13 @@ export interface BioApiJobResponse { * @type {string} * @memberof BioApiJobResponse */ - job_id?: string; + jobId?: string; /** * * @type {string} * @memberof BioApiJobResponse */ - created_at?: string; + createdAt?: string; /** * * @type {BioApiStatus} @@ -46,8 +46,8 @@ export interface BioApiJobResponse { export function BioApiJobResponseFromJSON(json: any): BioApiJobResponse { return { - 'job_id': !exists(json, 'job_id') ? undefined : json['job_id'], - 'created_at': !exists(json, 'created_at') ? undefined : json['created_at'], + 'jobId': !exists(json, 'jobId') ? undefined : json['jobId'], + 'createdAt': !exists(json, 'createdAt') ? undefined : json['createdAt'], 'status': !exists(json, 'status') ? undefined : BioApiStatusFromJSON(json['status']), }; } @@ -57,8 +57,8 @@ export function BioApiJobResponseToJSON(value?: BioApiJobResponse): any { return undefined; } return { - 'job_id': value.job_id, - 'created_at': value.created_at, + 'jobId': value.jobId, + 'createdAt': value.createdAt, 'status': BioApiStatusToJSON(value.status), }; } diff --git a/openapi_specs/SOFI/SOFI.yaml b/openapi_specs/SOFI/SOFI.yaml index 55a89ce5..ee36724d 100644 --- a/openapi_specs/SOFI/SOFI.yaml +++ b/openapi_specs/SOFI/SOFI.yaml @@ -1317,10 +1317,10 @@ components: BioApiJobResponse: properties: - job_id: + jobId: type: string title: Job Id - created_at: + createdAt: type: string title: Created At status: diff --git a/web/openapi_specs/SOFI/SOFI.yaml b/web/openapi_specs/SOFI/SOFI.yaml index 55a89ce5..ee36724d 100644 --- a/web/openapi_specs/SOFI/SOFI.yaml +++ b/web/openapi_specs/SOFI/SOFI.yaml @@ -1317,10 +1317,10 @@ components: BioApiJobResponse: properties: - job_id: + jobId: type: string title: Job Id - created_at: + createdAt: type: string title: Created At status: diff --git a/web/src/SAP/generated/models/bio_api_job_response.py b/web/src/SAP/generated/models/bio_api_job_response.py index 22641edc..c2f10b56 100644 --- a/web/src/SAP/generated/models/bio_api_job_response.py +++ b/web/src/SAP/generated/models/bio_api_job_response.py @@ -36,8 +36,8 @@ def __init__(self, job_id=None, created_at=None, status=None): # noqa: E501 } self.attribute_map = { - 'job_id': 'job_id', - 'created_at': 'created_at', + 'job_id': 'jobId', + 'created_at': 'createdAt', 'status': 'status', } diff --git a/web/src/SAP/generated/openapi/openapi.yaml b/web/src/SAP/generated/openapi/openapi.yaml index 8c98136f..cc505908 100644 --- a/web/src/SAP/generated/openapi/openapi.yaml +++ b/web/src/SAP/generated/openapi/openapi.yaml @@ -1290,13 +1290,13 @@ components: type: object BioApiJobResponse: example: - job_id: job_id - created_at: created_at + jobId: jobId + createdAt: createdAt properties: - job_id: + jobId: title: Job Id type: string - created_at: + createdAt: title: Created At type: string status: diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 49b8566c..3d26aecb 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -16,4 +16,6 @@ def post(user, token, body: NearestNeighborsRequest): request = BioNearestNeighborsRequest("samples", filtering, "categories.cgmlst.report.alleles", body.id, body.cutoff, body.unknowns_are_diffs) api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) - return jsonify(api_response.to_dict()) + return jsonify({"status": api_response.status.value, + "jobId": api_response.job_id, + "createdAt":api_response.created_at }) From c2aea30b4dc99fb406e6c4f41039fe089c4cbdf9 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 09:44:12 +0200 Subject: [PATCH 08/18] fix: added dispatch to dependency array --- .../app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index eb4bae89..fee6a177 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -53,7 +53,7 @@ export const NearestNeighborModal = (props: Props) => { }), }) ); - }, [setIsSearching, selection, cutoff, unknownsAreDiffs]); + }, [setIsSearching, selection, cutoff, unknownsAreDiffs, dispatch]); return ( From 435217ea72fe4cb7c93878b98beda31131d96765 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 13:51:34 +0200 Subject: [PATCH 09/18] feat: show toast with number of neighbors found --- .../nearest-neighbor-modal.tsx | 57 +++++++++++++------ .../nearest-neighbor-query-configs.ts | 26 +++++---- .../sap-client/models/BioApiJobResponse.ts | 11 ++++ openapi_specs/SOFI/SOFI.yaml | 6 ++ openapi_specs/bio_api.yaml | 14 ++--- web/openapi_specs/SOFI/SOFI.yaml | 6 ++ web/openapi_specs/bio_api.yaml | 14 ++--- .../generated/models/bio_api_job_response.py | 30 +++++++++- web/src/SAP/generated/openapi/openapi.yaml | 9 +++ .../controllers/NearestNeighborsController.py | 31 +++++++++- web/src/SAP/src/repositories/analysis.py | 1 + .../model/nearest_neighbors_get_response.py | 14 +++-- 12 files changed, 166 insertions(+), 53 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index fee6a177..a7252593 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -6,6 +6,7 @@ import { NumberInputField, NumberInputStepper, Spinner, + useToast, } from "@chakra-ui/react"; import { AnalysisResult } from "sap-client"; import { DataTableSelection } from "../data-table/data-table"; @@ -20,10 +21,13 @@ import { ModalCloseButton, } from "@chakra-ui/react"; import { useTranslation } from "react-i18next"; -import { useDispatch } from "react-redux"; -import { requestAsync } from "redux-query"; -import { nearestNeighborsRequest } from "./nearest-neighbor-query-configs"; +import { useDispatch, useSelector } from "react-redux"; +import { + NearestNeighborsResponse, + getNearestNeighbors, +} from "./nearest-neighbor-query-configs"; import { Checkbox } from "@chakra-ui/react"; +import { useMutation } from "redux-query-react"; type Props = { selection: DataTableSelection; @@ -37,24 +41,42 @@ export const NearestNeighborModal = (props: Props) => { const [cutoff, setCutoff] = React.useState(15); const onChangeCutoff = (value: string) => setCutoff(parseInt(value)); const [unknownsAreDiffs, setUnknownsAreDiffs] = useState(true); - + const toast = useToast(); const dispatch = useDispatch(); - const onSearch = useCallback(() => { - setIsSearching(true); + const nnResponse = useSelector( + (state: { entities: NearestNeighborsResponse }) => state.entities.nnResponse + ); + const [{ isPending, status }, searchNearestNeighbors] = useMutation(() => { const first = Object.values(selection)[0]; - dispatch( - requestAsync({ - ...nearestNeighborsRequest({ - id: first.original.id, - cutoff, - unknownsAreDiffs, - }), - }) - ); + + const req = { + id: first.original.id, + cutoff, + unknownsAreDiffs, + }; + return getNearestNeighbors(req); + }); + + const onSearch = useCallback(async () => { + setIsSearching(true); + await searchNearestNeighbors(); }, [setIsSearching, selection, cutoff, unknownsAreDiffs, dispatch]); + React.useEffect(() => { + if (isSearching && status >= 200 && status < 300 && !isPending) { + console.dir(nnResponse); + toast({ + title: `Found ${nnResponse.result.length} neighbor(s)`, + status: "success", + duration: 5000, + isClosable: true, + }); + onClose(); + } + }, [t, toast, isSearching, isPending, status, nnResponse]); + return ( @@ -91,8 +113,9 @@ export const NearestNeighborModal = (props: Props) => { ) : ( - // TODO: unknowns_are_diffs - + <> + + )}
diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts index 5cf4c06f..72158ea4 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts @@ -1,22 +1,26 @@ -import { BioApiJobResponse, NearestNeighborsRequest, post } from "sap-client"; +import { post, BioApiJobResponse, NearestNeighborsRequest } from "sap-client"; import { getUrl } from "service"; -// TODO -export const nearestNeighborsRequest = (request: NearestNeighborsRequest) => { - const base = post({ body: request }); +export type NearestNeighborsResponse = { + nnResponse: BioApiJobResponse; +}; - base.url = getUrl(base.url); +export const getNearestNeighbors = (params: NearestNeighborsRequest) => { + const base = post({ body: params }); + base.url = getUrl(base.url); base.transform = (response: BioApiJobResponse) => { - return {}; + return { + nnResponse: response, + }; }; - // base.update = { - // health: (oldValue, newValue) => { - // return merge({}, cloneDeep(oldValue), newValue); - // }, - // }; + base.update = { + nnResponse: (_, newValue) => newValue, + }; + // Force a network call to be made. Making it promise as well. base.force = true; + return base; }; diff --git a/app/src/sap-client/models/BioApiJobResponse.ts b/app/src/sap-client/models/BioApiJobResponse.ts index a54e6ece..7b6df647 100644 --- a/app/src/sap-client/models/BioApiJobResponse.ts +++ b/app/src/sap-client/models/BioApiJobResponse.ts @@ -13,6 +13,9 @@ import { exists, mapValues } from '../runtime'; import { + AnalysisResult, + AnalysisResultFromJSON, + AnalysisResultToJSON, BioApiStatus, BioApiStatusFromJSON, BioApiStatusToJSON, @@ -42,6 +45,12 @@ export interface BioApiJobResponse { * @memberof BioApiJobResponse */ status?: BioApiStatus; + /** + * + * @type {Array} + * @memberof BioApiJobResponse + */ + result?: Array; } export function BioApiJobResponseFromJSON(json: any): BioApiJobResponse { @@ -49,6 +58,7 @@ export function BioApiJobResponseFromJSON(json: any): BioApiJobResponse { 'jobId': !exists(json, 'jobId') ? undefined : json['jobId'], 'createdAt': !exists(json, 'createdAt') ? undefined : json['createdAt'], 'status': !exists(json, 'status') ? undefined : BioApiStatusFromJSON(json['status']), + 'result': !exists(json, 'result') ? undefined : (json['result'] as Array).map(AnalysisResultFromJSON), }; } @@ -60,6 +70,7 @@ export function BioApiJobResponseToJSON(value?: BioApiJobResponse): any { 'jobId': value.jobId, 'createdAt': value.createdAt, 'status': BioApiStatusToJSON(value.status), + 'result': value.result === undefined ? undefined : (value.result as Array).map(AnalysisResultToJSON), }; } diff --git a/openapi_specs/SOFI/SOFI.yaml b/openapi_specs/SOFI/SOFI.yaml index ee36724d..a45ef681 100644 --- a/openapi_specs/SOFI/SOFI.yaml +++ b/openapi_specs/SOFI/SOFI.yaml @@ -1325,6 +1325,12 @@ components: title: Created At status: "$ref": "#/components/schemas/BioApiStatus" + result: + type: array + items: + "$ref": "#/components/schemas/AnalysisResult" + title: Result + nullable: true BioApiStatus: type: string diff --git a/openapi_specs/bio_api.yaml b/openapi_specs/bio_api.yaml index 480ff2aa..11baf772 100644 --- a/openapi_specs/bio_api.yaml +++ b/openapi_specs/bio_api.yaml @@ -457,9 +457,8 @@ components: status: "$ref": "#/components/schemas/Status" finished_at: - anyOf: - - type: string - - type: 'null' + type: string + nullable: true title: Finished At seq_collection: type: string @@ -480,12 +479,11 @@ components: type: boolean title: Unknowns Are Diffs result: - anyOf: - - items: - "$ref": "#/components/schemas/Neighbor" - type: array - - type: 'null' + type: array + items: + "$ref": "#/components/schemas/Neighbor" title: Result + nullable: true type: object required: - job_id diff --git a/web/openapi_specs/SOFI/SOFI.yaml b/web/openapi_specs/SOFI/SOFI.yaml index ee36724d..a45ef681 100644 --- a/web/openapi_specs/SOFI/SOFI.yaml +++ b/web/openapi_specs/SOFI/SOFI.yaml @@ -1325,6 +1325,12 @@ components: title: Created At status: "$ref": "#/components/schemas/BioApiStatus" + result: + type: array + items: + "$ref": "#/components/schemas/AnalysisResult" + title: Result + nullable: true BioApiStatus: type: string diff --git a/web/openapi_specs/bio_api.yaml b/web/openapi_specs/bio_api.yaml index 480ff2aa..11baf772 100644 --- a/web/openapi_specs/bio_api.yaml +++ b/web/openapi_specs/bio_api.yaml @@ -457,9 +457,8 @@ components: status: "$ref": "#/components/schemas/Status" finished_at: - anyOf: - - type: string - - type: 'null' + type: string + nullable: true title: Finished At seq_collection: type: string @@ -480,12 +479,11 @@ components: type: boolean title: Unknowns Are Diffs result: - anyOf: - - items: - "$ref": "#/components/schemas/Neighbor" - type: array - - type: 'null' + type: array + items: + "$ref": "#/components/schemas/Neighbor" title: Result + nullable: true type: object required: - job_id diff --git a/web/src/SAP/generated/models/bio_api_job_response.py b/web/src/SAP/generated/models/bio_api_job_response.py index c2f10b56..df0a6df5 100644 --- a/web/src/SAP/generated/models/bio_api_job_response.py +++ b/web/src/SAP/generated/models/bio_api_job_response.py @@ -6,9 +6,11 @@ from typing import List, Dict # noqa: F401 from web.src.SAP.generated.models.base_model_ import Model +from web.src.SAP.generated.models.analysis_result import AnalysisResult from web.src.SAP.generated.models.bio_api_status import BioApiStatus from web.src.SAP.generated import util +from web.src.SAP.generated.models.analysis_result import AnalysisResult # noqa: E501 from web.src.SAP.generated.models.bio_api_status import BioApiStatus # noqa: E501 class BioApiJobResponse(Model): @@ -19,7 +21,7 @@ class BioApiJobResponse(Model): Do not edit the class manually. """ - def __init__(self, job_id=None, created_at=None, status=None): # noqa: E501 + def __init__(self, job_id=None, created_at=None, status=None, result=None): # noqa: E501 """BioApiJobResponse - a model defined in OpenAPI :param job_id: The job_id of this BioApiJobResponse. # noqa: E501 @@ -28,22 +30,27 @@ def __init__(self, job_id=None, created_at=None, status=None): # noqa: E501 :type created_at: str :param status: The status of this BioApiJobResponse. # noqa: E501 :type status: BioApiStatus + :param result: The result of this BioApiJobResponse. # noqa: E501 + :type result: List[AnalysisResult] """ self.openapi_types = { 'job_id': str, 'created_at': str, 'status': BioApiStatus, + 'result': List[AnalysisResult], } self.attribute_map = { 'job_id': 'jobId', 'created_at': 'createdAt', 'status': 'status', + 'result': 'result', } self._job_id = job_id self._created_at = created_at self._status = status + self._result = result @classmethod def from_dict(cls, dikt): @@ -118,3 +125,24 @@ def status(self, status): """ self._status = status + + @property + def result(self): + """Gets the result of this BioApiJobResponse. + + + :return: The result of this BioApiJobResponse. + :rtype: List[AnalysisResult] + """ + return self._result + + @result.setter + def result(self, result): + """Sets the result of this BioApiJobResponse. + + + :param result: The result of this BioApiJobResponse. + :type result: List[AnalysisResult] + """ + + self._result = result diff --git a/web/src/SAP/generated/openapi/openapi.yaml b/web/src/SAP/generated/openapi/openapi.yaml index cc505908..5631d717 100644 --- a/web/src/SAP/generated/openapi/openapi.yaml +++ b/web/src/SAP/generated/openapi/openapi.yaml @@ -1290,6 +1290,9 @@ components: type: object BioApiJobResponse: example: + result: + - null + - null jobId: jobId createdAt: createdAt properties: @@ -1301,6 +1304,12 @@ components: type: string status: $ref: '#/components/schemas/BioApiStatus' + result: + items: + $ref: '#/components/schemas/AnalysisResult' + nullable: true + title: Result + type: array BioApiStatus: enum: - init diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 3d26aecb..00861ca0 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -1,5 +1,9 @@ +import sys +import time from web.src.SAP.generated.models.nearest_neighbors_request import NearestNeighborsRequest -from web.src.SAP.src.repositories.analysis import get_single_analysis_by_object_id +from web.src.SAP.src.controllers.AnalysisController import get_sequence_by_id +from web.src.SAP.src.repositories.analysis import get_analysis_with_metadata, get_single_analysis_by_object_id +from web.src.SAP.src.security.permission_check import assert_user_has from web.src.services.bio_api.openapi.api.nearest_neighbors_api import NearestNeighborsApi from web.src.services.bio_api.openapi.api_client import ApiClient from web.src.services.bio_api.openapi.configuration import Configuration @@ -8,6 +12,8 @@ from flask.json import jsonify def post(user, token, body: NearestNeighborsRequest): + assert_user_has("search", token) + with ApiClient(Configuration(host="http://bio_api:8000")) as api_client: analysis = get_single_analysis_by_object_id(body.id) @@ -16,6 +22,27 @@ def post(user, token, body: NearestNeighborsRequest): request = BioNearestNeighborsRequest("samples", filtering, "categories.cgmlst.report.alleles", body.id, body.cutoff, body.unknowns_are_diffs) api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) + job_id = api_response.job_id + status = api_response.status.value + + while status == "init": + time.sleep(2) + api_response = api_instance.nn_result_v1_nearest_neighbors_nn_id_get(job_id) + status = api_response.status + + if status == "error": + return jsonify({"status": "error"}) + + response_dict = api_response.to_dict() + + def get_result(id: str): + row = get_single_analysis_by_object_id(id) + + return get_analysis_with_metadata(row["sequence_id"]) + + result = list(map(lambda r : get_result(r["id"]), response_dict["result"])) + return jsonify({"status": api_response.status.value, "jobId": api_response.job_id, - "createdAt":api_response.created_at }) + "createdAt": api_response.created_at, + "result": result }) diff --git a/web/src/SAP/src/repositories/analysis.py b/web/src/SAP/src/repositories/analysis.py index 5c1061d2..87be4871 100644 --- a/web/src/SAP/src/repositories/analysis.py +++ b/web/src/SAP/src/repositories/analysis.py @@ -174,6 +174,7 @@ def get_analysis_with_metadata(sequence_id: str) -> Dict[str, Any]: } } }, + {"$set": { "id": {"$toString": "$_id"}}}, {"$unset": ["_id", "metadata"]}, {"$limit": (int(1))}, ] diff --git a/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py b/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py index ef764f7a..64c06857 100644 --- a/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py +++ b/web/src/services/bio_api/openapi/model/nearest_neighbors_get_response.py @@ -30,7 +30,9 @@ def lazy_import(): + from web.src.services.bio_api.openapi.model.neighbor import Neighbor from web.src.services.bio_api.openapi.model.status import Status + globals()['Neighbor'] = Neighbor globals()['Status'] = Status @@ -90,14 +92,14 @@ def openapi_types(): 'job_id': (str,), # noqa: E501 'created_at': (str,), # noqa: E501 'status': (Status,), # noqa: E501 - 'finished_at': (dict,), # noqa: E501 + 'finished_at': (str, none_type,), # noqa: E501 'seq_collection': (str,), # noqa: E501 'filtering': ({str: (bool, date, datetime, dict, float, int, list, str, none_type)},), # noqa: E501 'profile_field_path': (str,), # noqa: E501 'input_mongo_id': (str,), # noqa: E501 'cutoff': (int,), # noqa: E501 'unknowns_are_diffs': (bool,), # noqa: E501 - 'result': (dict,), # noqa: E501 + 'result': ([Neighbor], none_type,), # noqa: E501 } @cached_property @@ -133,14 +135,14 @@ def _from_openapi_data(cls, job_id, created_at, status, finished_at, seq_collect job_id (str): created_at (str): status (Status): - finished_at (dict): + finished_at (str, none_type): seq_collection (str): filtering ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): profile_field_path (str): input_mongo_id (str): cutoff (int): unknowns_are_diffs (bool): - result (dict): + result ([Neighbor], none_type): Keyword Args: _check_type (bool): if True, values for parameters in openapi_types @@ -238,14 +240,14 @@ def __init__(self, job_id, created_at, status, finished_at, seq_collection, filt job_id (str): created_at (str): status (Status): - finished_at (dict): + finished_at (str, none_type): seq_collection (str): filtering ({str: (bool, date, datetime, dict, float, int, list, str, none_type)}): profile_field_path (str): input_mongo_id (str): cutoff (int): unknowns_are_diffs (bool): - result (dict): + result ([Neighbor], none_type): Keyword Args: _check_type (bool): if True, values for parameters in openapi_types From 80449c9e46e276d638002f98db268467ada2c334 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Thu, 16 May 2024 14:12:40 +0200 Subject: [PATCH 10/18] fix: dependency arrays --- .../app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index a7252593..1450d35f 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -62,7 +62,7 @@ export const NearestNeighborModal = (props: Props) => { const onSearch = useCallback(async () => { setIsSearching(true); await searchNearestNeighbors(); - }, [setIsSearching, selection, cutoff, unknownsAreDiffs, dispatch]); + }, [setIsSearching, searchNearestNeighbors]); React.useEffect(() => { if (isSearching && status >= 200 && status < 300 && !isPending) { @@ -75,7 +75,7 @@ export const NearestNeighborModal = (props: Props) => { }); onClose(); } - }, [t, toast, isSearching, isPending, status, nnResponse]); + }, [t, toast, isSearching, isPending, status, nnResponse, onClose]); return ( From 01a543acbfad57ec19e8bac81576019ad12f3e4a Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Fri, 17 May 2024 07:31:17 +0200 Subject: [PATCH 11/18] fix: nn use detected_species from sample --- web/src/SAP/src/controllers/NearestNeighborsController.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 00861ca0..01500220 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -1,8 +1,8 @@ import sys import time from web.src.SAP.generated.models.nearest_neighbors_request import NearestNeighborsRequest -from web.src.SAP.src.controllers.AnalysisController import get_sequence_by_id from web.src.SAP.src.repositories.analysis import get_analysis_with_metadata, get_single_analysis_by_object_id +from web.src.SAP.src.repositories.samples import get_single_sample_by_object_id from web.src.SAP.src.security.permission_check import assert_user_has from web.src.services.bio_api.openapi.api.nearest_neighbors_api import NearestNeighborsApi from web.src.services.bio_api.openapi.api_client import ApiClient @@ -15,10 +15,11 @@ def post(user, token, body: NearestNeighborsRequest): assert_user_has("search", token) with ApiClient(Configuration(host="http://bio_api:8000")) as api_client: - analysis = get_single_analysis_by_object_id(body.id) + sample = get_single_sample_by_object_id(body.id) + detected_species = sample["categories"]["species_detection"]["summary"]["detected_species"] api_instance = NearestNeighborsApi(api_client) - filtering = {"categories.species_detection.summary.detected_species": analysis["species_final"]} + filtering = {"categories.species_detection.summary.detected_species": detected_species} request = BioNearestNeighborsRequest("samples", filtering, "categories.cgmlst.report.alleles", body.id, body.cutoff, body.unknowns_are_diffs) api_response = api_instance.nearest_neighbors_v1_nearest_neighbors_post(request) From bf1b854746ef7202cfa80834b72234cc742a6b68 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Fri, 17 May 2024 08:27:32 +0200 Subject: [PATCH 12/18] feat: add nn result to selection --- app/src/app/analysis/analysis-page.tsx | 4 +- .../app/analysis/data-table/data-table.tsx | 2 +- .../nearest-neighbor-modal.tsx | 37 +++++++++++++++---- .../nearest-neighbor-query-configs.ts | 10 ++--- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/app/src/app/analysis/analysis-page.tsx b/app/src/app/analysis/analysis-page.tsx index 86d9c13e..480a2840 100644 --- a/app/src/app/analysis/analysis-page.tsx +++ b/app/src/app/analysis/analysis-page.tsx @@ -669,7 +669,9 @@ export default function AnalysisPage() {

redux selection:

- {JSON.stringify(selection, undefined, " ")} +
+          {JSON.stringify(selection, undefined, "  ")}
+        
); diff --git a/app/src/app/analysis/data-table/data-table.tsx b/app/src/app/analysis/data-table/data-table.tsx index 6c1c57c2..8c18311c 100644 --- a/app/src/app/analysis/data-table/data-table.tsx +++ b/app/src/app/analysis/data-table/data-table.tsx @@ -37,7 +37,7 @@ export type DataTableSelection = Record< string, { original: T; - cells: Record; + cells: Partial>; } >; diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index 1450d35f..4a8862e9 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useState } from "react"; import { NumberDecrementStepper, NumberIncrementStepper, @@ -23,11 +23,12 @@ import { import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; import { - NearestNeighborsResponse, + NearestNeighborsResponseSlice, getNearestNeighbors, } from "./nearest-neighbor-query-configs"; import { Checkbox } from "@chakra-ui/react"; import { useMutation } from "redux-query-react"; +import { setSelection } from "../analysis-selection-configs"; type Props = { selection: DataTableSelection; @@ -44,8 +45,9 @@ export const NearestNeighborModal = (props: Props) => { const toast = useToast(); const dispatch = useDispatch(); - const nnResponse = useSelector( - (state: { entities: NearestNeighborsResponse }) => state.entities.nnResponse + const nearestNeighborsResponse = useSelector( + (state: { entities: NearestNeighborsResponseSlice }) => + state.entities.nearestNeighborsResponse ); const [{ isPending, status }, searchNearestNeighbors] = useMutation(() => { @@ -66,16 +68,37 @@ export const NearestNeighborModal = (props: Props) => { React.useEffect(() => { if (isSearching && status >= 200 && status < 300 && !isPending) { - console.dir(nnResponse); toast({ - title: `Found ${nnResponse.result.length} neighbor(s)`, + title: `Found ${nearestNeighborsResponse.result.length} neighbor(s)`, status: "success", duration: 5000, isClosable: true, }); + + if (nearestNeighborsResponse.result) { + const newSelection = Object.assign({}, selection); + nearestNeighborsResponse.result?.forEach((n) => { + newSelection[n.sequence_id] = { + cells: {}, + original: n, + }; + }); + dispatch(setSelection(newSelection)); + } + onClose(); } - }, [t, toast, isSearching, isPending, status, nnResponse, onClose]); + }, [ + t, + toast, + isSearching, + isPending, + status, + nearestNeighborsResponse, + onClose, + dispatch, + selection, + ]); return ( diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts index 72158ea4..ca0ebf71 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts @@ -1,22 +1,22 @@ import { post, BioApiJobResponse, NearestNeighborsRequest } from "sap-client"; import { getUrl } from "service"; -export type NearestNeighborsResponse = { - nnResponse: BioApiJobResponse; +export type NearestNeighborsResponseSlice = { + nearestNeighborsResponse: BioApiJobResponse; }; export const getNearestNeighbors = (params: NearestNeighborsRequest) => { - const base = post({ body: params }); + const base = post({ body: params }); base.url = getUrl(base.url); base.transform = (response: BioApiJobResponse) => { return { - nnResponse: response, + nearestNeighborsResponse: response, }; }; base.update = { - nnResponse: (_, newValue) => newValue, + nearestNeighborsResponse: (_, newValue) => newValue, }; // Force a network call to be made. Making it promise as well. From d9d6988d7dceffdc0873b2f7f507a97e6641eb7a Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Fri, 17 May 2024 10:08:46 +0200 Subject: [PATCH 13/18] feat: nn error toast --- .../nearest-neighbor-modal.tsx | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index 4a8862e9..31feee9a 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -67,23 +67,32 @@ export const NearestNeighborModal = (props: Props) => { }, [setIsSearching, searchNearestNeighbors]); React.useEffect(() => { - if (isSearching && status >= 200 && status < 300 && !isPending) { - toast({ - title: `Found ${nearestNeighborsResponse.result.length} neighbor(s)`, - status: "success", - duration: 5000, - isClosable: true, - }); + if (isSearching && !isPending) { + if (status >= 200 && status < 300) { + toast({ + title: `Found ${nearestNeighborsResponse.result.length} neighbor(s)`, + status: "success", + duration: 5000, + isClosable: true, + }); - if (nearestNeighborsResponse.result) { - const newSelection = Object.assign({}, selection); - nearestNeighborsResponse.result?.forEach((n) => { - newSelection[n.sequence_id] = { - cells: {}, - original: n, - }; + if (nearestNeighborsResponse.result) { + const newSelection = Object.assign({}, selection); + nearestNeighborsResponse.result?.forEach((n) => { + newSelection[n.sequence_id] = { + cells: {}, + original: n, + }; + }); + dispatch(setSelection(newSelection)); + } + } else { + toast({ + title: `Error`, + status: "error", + duration: 5000, + isClosable: true, }); - dispatch(setSelection(newSelection)); } onClose(); From e83b47c08265838af89b36fd8b44a629a8d12717 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Fri, 17 May 2024 10:09:11 +0200 Subject: [PATCH 14/18] feat: clear selection menu item added --- .../app/analysis/analysis-selection-menu.tsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/app/analysis/analysis-selection-menu.tsx b/app/src/app/analysis/analysis-selection-menu.tsx index 0cb843e8..dfbab6b7 100644 --- a/app/src/app/analysis/analysis-selection-menu.tsx +++ b/app/src/app/analysis/analysis-selection-menu.tsx @@ -1,9 +1,12 @@ import { AnalysisResult } from "sap-client"; import { DataTableSelection } from "./data-table/data-table"; -import { HamburgerIcon } from "@chakra-ui/icons"; +import { HamburgerIcon, SmallCloseIcon } from "@chakra-ui/icons"; import { ResistanceMenuItem } from "./resistance/resistance-menu-item"; import { NearestNeighborMenuItem } from "./nearest-neighbor/nearest-neighbor-menu-item"; -import { Menu, MenuList, MenuButton, Button } from "@chakra-ui/react"; +import { Menu, MenuList, MenuButton, Button, MenuItem } from "@chakra-ui/react"; +import { useCallback } from "react"; +import { clearSelection } from "./analysis-selection-configs"; +import { useDispatch } from "react-redux"; type Props = { selection: DataTableSelection; @@ -12,6 +15,11 @@ type Props = { export const AnalysisSelectionMenu = (props: Props) => { const { selection, isNarrowed } = props; + const dispatch = useDispatch(); + + const onClear = useCallback(() => { + dispatch(clearSelection()); + }, [dispatch]); return (
@@ -26,6 +34,14 @@ export const AnalysisSelectionMenu = (props: Props) => { + } + onClick={onClear} + > + Clear Selection +
From 32cab7acae506f9a89f65b35e615c37dd0821c52 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Fri, 17 May 2024 10:11:03 +0200 Subject: [PATCH 15/18] feat: maintain selection in root state --- app/src/app/analysis/analysis-page.tsx | 9 +-- .../app/analysis/data-table/data-table.tsx | 69 +++++++++---------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/app/src/app/analysis/analysis-page.tsx b/app/src/app/analysis/analysis-page.tsx index 480a2840..3a0f224c 100644 --- a/app/src/app/analysis/analysis-page.tsx +++ b/app/src/app/analysis/analysis-page.tsx @@ -572,7 +572,9 @@ export default function AnalysisPage() { ); const onSelectCallback = React.useCallback( - (sel) => dispatch(setSelection(sel)), + (sel) => { + dispatch(setSelection(sel)); + }, [dispatch] ); @@ -648,6 +650,7 @@ export default function AnalysisPage() { pageState.isNarrowed ? "approvingCell" : "selectedCell" } onSelect={onSelectCallback} + selection={selection} onDetailsClick={openDetailsView} view={view} /> @@ -669,9 +672,7 @@ export default function AnalysisPage() {

redux selection:

-
-          {JSON.stringify(selection, undefined, "  ")}
-        
+
{JSON.stringify(selection, undefined, "  ")}
); diff --git a/app/src/app/analysis/data-table/data-table.tsx b/app/src/app/analysis/data-table/data-table.tsx index 8c18311c..f59e1502 100644 --- a/app/src/app/analysis/data-table/data-table.tsx +++ b/app/src/app/analysis/data-table/data-table.tsx @@ -54,6 +54,7 @@ type DataTableProps = { selectionClassName: string; approvableColumns: string[]; onSelect: (sel: DataTableSelection) => void; + selection: DataTableSelection; onDetailsClick: (isolateId: string, row: Row) => void; view: UserDefinedViewInternal; getCellStyle: ( @@ -73,7 +74,7 @@ type DataTableProps = { }; function DataTable(props: DataTableProps) { - const datagridRef = React.useRef(null); + const dataGridRef = React.useRef(null); const defaultColumn = React.useMemo( () => ({ @@ -102,12 +103,12 @@ function DataTable(props: DataTableProps) { getStickyCellStyle, renderCellControl, view, + selection, } = props; - const selection = React.useRef({} as DataTableSelection); const isInSelection = React.useCallback((rowId, columnId) => { - return selection.current[rowId]?.cells?.[columnId]; - }, []); + return selection[rowId]?.cells?.[columnId]; + }, [selection]); const [lastView, setLastView] = useState(view); @@ -121,35 +122,34 @@ function DataTable(props: DataTableProps) { merged = (view as unknown) as TableState; setLastView(view); // force columns to resize - datagridRef.current.resetAfterColumnIndex(0); + dataGridRef.current.resetAfterColumnIndex(0); } return merged as TableState; }, - [view, lastView, setLastView, datagridRef] + [view, lastView, setLastView, dataGridRef] ); const onSelectCell = React.useCallback( (rowId, columnId) => { const incSel: DataTableSelection = { - ...selection.current, + ...selection, [rowId]: { original: data.find((d) => d[primaryKey] === rowId), cells: { - ...(selection.current[rowId]?.cells || {}), + ...(selection[rowId]?.cells || {}), [columnId]: !isInSelection(rowId, columnId), }, }, }; getDependentColumns(columnId).forEach((v) => { incSel[rowId].cells[v] = !( - selection.current[rowId]?.cells && - selection.current[rowId]?.cells[columnId] + selection[rowId]?.cells && + selection[rowId]?.cells[columnId] ); }); - selection.current = incSel; onSelect(incSel); }, - [onSelect, isInSelection, getDependentColumns, data, primaryKey] + [onSelect, isInSelection, getDependentColumns, data, primaryKey, selection] ); const { @@ -206,7 +206,7 @@ function DataTable(props: DataTableProps) { const { sourceIdx, destIdx, targetId } = columnReordering; order.splice(sourceIdx, 1); order.splice(destIdx, 0, targetId); - datagridRef.current.resetAfterIndices({ + dataGridRef.current.resetAfterIndices({ columnIndex: destIdx < sourceIdx ? destIdx : sourceIdx, rowIndex: 1, shouldForceUpdate: false, @@ -239,13 +239,13 @@ function DataTable(props: DataTableProps) { // Narrow selection to the currently visible columns React.useEffect(() => { - if (selection.current) { - const selectionClone = { ...selection.current }; + if (selection) { + const selectionClone = { ...selection }; let needsNarrow = false; - Object.keys(selection.current) + Object.keys(selection) .filter((x) => typeof x === "string") .map((r) => { - Object.keys(selection.current[r].cells).map((k) => { + Object.keys(selection[r].cells).map((k) => { if ( visibleApprovableColumns.indexOf(k) < 0 && selectionClone[r][k] @@ -256,16 +256,15 @@ function DataTable(props: DataTableProps) { }); }); if (needsNarrow) { - selection.current = selectionClone; - onSelect(selection.current); + onSelect(selectionClone); } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [visibleApprovableColumns, onSelect, selection.current]); + }, [visibleApprovableColumns, onSelect, selection]); const calcRowSelectionState = React.useCallback( (row: Row) => { - const dataTableSelection = selection.current[row.original[primaryKey]]; + const dataTableSelection = selection[row.original[primaryKey]]; const checked = dataTableSelection !== undefined; const cells = dataTableSelection?.cells; const selectedCellCount = Object.values(cells ?? {}).reduce( @@ -283,12 +282,12 @@ function DataTable(props: DataTableProps) { // Else, some cells selected return { indeterminate: true, checked: false }; }, - [primaryKey, visibleApprovableColumns] + [primaryKey, visibleApprovableColumns, selection] ); const calcColSelectionState = React.useCallback( (col: Column) => { - const c = Object.values(selection.current).filter( + const c = Object.values(selection).filter( (x) => x.cells[col.id] === true ); if (c.length === 0) { @@ -299,7 +298,7 @@ function DataTable(props: DataTableProps) { } return { indeterminate: true, checked: false, visible: true }; }, - [rows] + [rows, selection] ); const onSelectRow = React.useCallback( @@ -308,7 +307,7 @@ function DataTable(props: DataTableProps) { const id = row.original[primaryKey]; const newSelection = { - ...selection.current, + ...selection, }; if (indeterminate || checked) { @@ -334,8 +333,7 @@ function DataTable(props: DataTableProps) { }; } - selection.current = newSelection; - onSelect(selection.current); + onSelect(newSelection); }, [ onSelect, @@ -345,16 +343,17 @@ function DataTable(props: DataTableProps) { isJudgedCell, calcRowSelectionState, canApproveColumn, + selection, ] ); const onColumnResize = React.useCallback( (colIndex: number) => { - if (datagridRef?.current?.resetAfterColumnIndex) { - datagridRef.current.resetAfterColumnIndex(colIndex); + if (dataGridRef?.current?.resetAfterColumnIndex) { + dataGridRef.current.resetAfterColumnIndex(colIndex); } }, - [datagridRef] + [dataGridRef] ); const onSelectColumn = React.useCallback( @@ -366,25 +365,24 @@ function DataTable(props: DataTableProps) { [r.original[primaryKey]]: { original: r.original, cells: { - ...selection.current[r.original[primaryKey]]?.cells, + ...selection[r.original[primaryKey]]?.cells, [col.id as string]: !checked && !indeterminate, }, }, })) .reduce((acc, val) => ({ ...acc, ...val })); - const incSel: DataTableSelection = { ...selection.current, ...sel }; + const incSel: DataTableSelection = { ...selection, ...sel }; getDependentColumns(col.id).forEach((c) => { rows .map((r) => r.original[primaryKey]) .forEach((r: string) => { if (incSel[r]) { incSel[r].cells[c] = !( - selection.current[r] && selection.current[r].cells[c] + selection[r] && selection[r].cells[c] ); } }); }); - selection.current = incSel; onSelect(incSel); onColumnResize(0); }, @@ -396,6 +394,7 @@ function DataTable(props: DataTableProps) { isJudgedCell, getDependentColumns, onColumnResize, + selection, ] ); @@ -549,7 +548,7 @@ function DataTable(props: DataTableProps) { itemData={itemData} rowCount={itemData.rows.length} height={height} - gridRef={datagridRef} + gridRef={dataGridRef} rowHeight={() => 35} estimatedRowHeight={35} columnWidth={getColumnWidth} From 5aa7d6a3a815669a3216b7e3b0f3155ff6122ae0 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Fri, 17 May 2024 10:32:38 +0200 Subject: [PATCH 16/18] refactor: rename nn response schema --- .../nearest-neighbor-query-configs.ts | 6 +-- .../sap-client/apis/NearestNeighborsApi.ts | 12 ++--- ...esponse.ts => NearestNeighborsResponse.ts} | 16 +++---- app/src/sap-client/models/index.ts | 2 +- openapi_specs/SOFI/SOFI.yaml | 4 +- web/openapi_specs/SOFI/SOFI.yaml | 4 +- .../nearest_neighbors_controller.py | 2 +- web/src/SAP/generated/models/__init__.py | 2 +- ...ponse.py => nearest_neighbors_response.py} | 48 +++++++++---------- web/src/SAP/generated/openapi/openapi.yaml | 4 +- .../test/test_nearest_neighbors_controller.py | 2 +- 11 files changed, 51 insertions(+), 51 deletions(-) rename app/src/sap-client/models/{BioApiJobResponse.ts => NearestNeighborsResponse.ts} (78%) rename web/src/SAP/generated/models/{bio_api_job_response.py => nearest_neighbors_response.py} (61%) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts index ca0ebf71..3b86d3f9 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts @@ -1,15 +1,15 @@ -import { post, BioApiJobResponse, NearestNeighborsRequest } from "sap-client"; +import { post, NearestNeighborsResponse, NearestNeighborsRequest } from "sap-client"; import { getUrl } from "service"; export type NearestNeighborsResponseSlice = { - nearestNeighborsResponse: BioApiJobResponse; + nearestNeighborsResponse: NearestNeighborsResponse; }; export const getNearestNeighbors = (params: NearestNeighborsRequest) => { const base = post({ body: params }); base.url = getUrl(base.url); - base.transform = (response: BioApiJobResponse) => { + base.transform = (response: NearestNeighborsResponse) => { return { nearestNeighborsResponse: response, }; diff --git a/app/src/sap-client/apis/NearestNeighborsApi.ts b/app/src/sap-client/apis/NearestNeighborsApi.ts index 45d1b6f4..c9c47d37 100644 --- a/app/src/sap-client/apis/NearestNeighborsApi.ts +++ b/app/src/sap-client/apis/NearestNeighborsApi.ts @@ -15,12 +15,12 @@ import { HttpMethods, QueryConfig, ResponseBody, ResponseText } from 'redux-query'; import * as runtime from '../runtime'; import { - BioApiJobResponse, - BioApiJobResponseFromJSON, - BioApiJobResponseToJSON, NearestNeighborsRequest, NearestNeighborsRequestFromJSON, NearestNeighborsRequestToJSON, + NearestNeighborsResponse, + NearestNeighborsResponseFromJSON, + NearestNeighborsResponseToJSON, } from '../models'; export interface PostRequest { @@ -31,7 +31,7 @@ export interface PostRequest { /** * Nearest Neighbors */ -function postRaw(requestParameters: PostRequest, requestConfig: runtime.TypedQueryConfig = {}): QueryConfig { +function postRaw(requestParameters: PostRequest, requestConfig: runtime.TypedQueryConfig = {}): QueryConfig { if (requestParameters.body === null || requestParameters.body === undefined) { throw new runtime.RequiredError('body','Required parameter requestParameters.body was null or undefined when calling post.'); } @@ -64,7 +64,7 @@ function postRaw(requestParameters: PostRequest, requestConfig: runtime.Typed const { transform: requestTransform } = requestConfig; if (requestTransform) { - config.transform = (body: ResponseBody, text: ResponseBody) => requestTransform(BioApiJobResponseFromJSON(body), text); + config.transform = (body: ResponseBody, text: ResponseBody) => requestTransform(NearestNeighborsResponseFromJSON(body), text); } return config; @@ -73,7 +73,7 @@ function postRaw(requestParameters: PostRequest, requestConfig: runtime.Typed /** * Nearest Neighbors */ -export function post(requestParameters: PostRequest, requestConfig?: runtime.TypedQueryConfig): QueryConfig { +export function post(requestParameters: PostRequest, requestConfig?: runtime.TypedQueryConfig): QueryConfig { return postRaw(requestParameters, requestConfig); } diff --git a/app/src/sap-client/models/BioApiJobResponse.ts b/app/src/sap-client/models/NearestNeighborsResponse.ts similarity index 78% rename from app/src/sap-client/models/BioApiJobResponse.ts rename to app/src/sap-client/models/NearestNeighborsResponse.ts index 7b6df647..a6849402 100644 --- a/app/src/sap-client/models/BioApiJobResponse.ts +++ b/app/src/sap-client/models/NearestNeighborsResponse.ts @@ -24,36 +24,36 @@ import { /** * * @export - * @interface BioApiJobResponse + * @interface NearestNeighborsResponse */ -export interface BioApiJobResponse { +export interface NearestNeighborsResponse { /** * * @type {string} - * @memberof BioApiJobResponse + * @memberof NearestNeighborsResponse */ jobId?: string; /** * * @type {string} - * @memberof BioApiJobResponse + * @memberof NearestNeighborsResponse */ createdAt?: string; /** * * @type {BioApiStatus} - * @memberof BioApiJobResponse + * @memberof NearestNeighborsResponse */ status?: BioApiStatus; /** * * @type {Array} - * @memberof BioApiJobResponse + * @memberof NearestNeighborsResponse */ result?: Array; } -export function BioApiJobResponseFromJSON(json: any): BioApiJobResponse { +export function NearestNeighborsResponseFromJSON(json: any): NearestNeighborsResponse { return { 'jobId': !exists(json, 'jobId') ? undefined : json['jobId'], 'createdAt': !exists(json, 'createdAt') ? undefined : json['createdAt'], @@ -62,7 +62,7 @@ export function BioApiJobResponseFromJSON(json: any): BioApiJobResponse { }; } -export function BioApiJobResponseToJSON(value?: BioApiJobResponse): any { +export function NearestNeighborsResponseToJSON(value?: NearestNeighborsResponse): any { if (value === undefined) { return undefined; } diff --git a/app/src/sap-client/models/index.ts b/app/src/sap-client/models/index.ts index 1714decb..0b8fb361 100644 --- a/app/src/sap-client/models/index.ts +++ b/app/src/sap-client/models/index.ts @@ -7,7 +7,6 @@ export * from './ApprovalAllOf'; export * from './ApprovalRequest'; export * from './ApprovalStatus'; export * from './BaseMetadata'; -export * from './BioApiJobResponse'; export * from './BioApiStatus'; export * from './Column'; export * from './DataClearance'; @@ -20,6 +19,7 @@ export * from './LimsSpecificMetadata'; export * from './MetadataReloadRequest'; export * from './MetadataReloadResponse'; export * from './NearestNeighborsRequest'; +export * from './NearestNeighborsResponse'; export * from './NewickTreeResponse'; export * from './Organization'; export * from './PageOfAnalysis'; diff --git a/openapi_specs/SOFI/SOFI.yaml b/openapi_specs/SOFI/SOFI.yaml index a45ef681..3040727d 100644 --- a/openapi_specs/SOFI/SOFI.yaml +++ b/openapi_specs/SOFI/SOFI.yaml @@ -34,7 +34,7 @@ paths: content: application/json: schema: - "$ref": "#/components/schemas/BioApiJobResponse" + "$ref": "#/components/schemas/NearestNeighborsResponse" /me: get: description: Describe the current user and their permissions @@ -1315,7 +1315,7 @@ components: title: NearestNeighborsRequest description: Parameters for a REST request for a nearest neighbors calculation. - BioApiJobResponse: + NearestNeighborsResponse: properties: jobId: type: string diff --git a/web/openapi_specs/SOFI/SOFI.yaml b/web/openapi_specs/SOFI/SOFI.yaml index a45ef681..3040727d 100644 --- a/web/openapi_specs/SOFI/SOFI.yaml +++ b/web/openapi_specs/SOFI/SOFI.yaml @@ -34,7 +34,7 @@ paths: content: application/json: schema: - "$ref": "#/components/schemas/BioApiJobResponse" + "$ref": "#/components/schemas/NearestNeighborsResponse" /me: get: description: Describe the current user and their permissions @@ -1315,7 +1315,7 @@ components: title: NearestNeighborsRequest description: Parameters for a REST request for a nearest neighbors calculation. - BioApiJobResponse: + NearestNeighborsResponse: properties: jobId: type: string diff --git a/web/src/SAP/generated/controllers/nearest_neighbors_controller.py b/web/src/SAP/generated/controllers/nearest_neighbors_controller.py index 03edf485..f1aa3515 100644 --- a/web/src/SAP/generated/controllers/nearest_neighbors_controller.py +++ b/web/src/SAP/generated/controllers/nearest_neighbors_controller.py @@ -12,7 +12,7 @@ def post(user, token_info, body): # noqa: E501 :param body: :type body: dict | bytes - :rtype: BioApiJobResponse + :rtype: NearestNeighborsResponse """ if connexion.request.is_json: from ..models import NearestNeighborsRequest diff --git a/web/src/SAP/generated/models/__init__.py b/web/src/SAP/generated/models/__init__.py index 625ae4da..b1a7d283 100644 --- a/web/src/SAP/generated/models/__init__.py +++ b/web/src/SAP/generated/models/__init__.py @@ -12,7 +12,6 @@ from .approval_request import ApprovalRequest from .approval_status import ApprovalStatus from .base_metadata import BaseMetadata -from .bio_api_job_response import BioApiJobResponse from .bio_api_status import BioApiStatus from .bulk_upload_request import BulkUploadRequest from .column import Column @@ -27,6 +26,7 @@ from .metadata_reload_response import MetadataReloadResponse from .multi_upload_request import MultiUploadRequest from .nearest_neighbors_request import NearestNeighborsRequest +from .nearest_neighbors_response import NearestNeighborsResponse from .newick_tree_response import NewickTreeResponse from .organization import Organization from .page_of_analysis import PageOfAnalysis diff --git a/web/src/SAP/generated/models/bio_api_job_response.py b/web/src/SAP/generated/models/nearest_neighbors_response.py similarity index 61% rename from web/src/SAP/generated/models/bio_api_job_response.py rename to web/src/SAP/generated/models/nearest_neighbors_response.py index df0a6df5..6016d715 100644 --- a/web/src/SAP/generated/models/bio_api_job_response.py +++ b/web/src/SAP/generated/models/nearest_neighbors_response.py @@ -13,7 +13,7 @@ from web.src.SAP.generated.models.analysis_result import AnalysisResult # noqa: E501 from web.src.SAP.generated.models.bio_api_status import BioApiStatus # noqa: E501 -class BioApiJobResponse(Model): +class NearestNeighborsResponse(Model): @@ -22,15 +22,15 @@ class BioApiJobResponse(Model): """ def __init__(self, job_id=None, created_at=None, status=None, result=None): # noqa: E501 - """BioApiJobResponse - a model defined in OpenAPI + """NearestNeighborsResponse - a model defined in OpenAPI - :param job_id: The job_id of this BioApiJobResponse. # noqa: E501 + :param job_id: The job_id of this NearestNeighborsResponse. # noqa: E501 :type job_id: str - :param created_at: The created_at of this BioApiJobResponse. # noqa: E501 + :param created_at: The created_at of this NearestNeighborsResponse. # noqa: E501 :type created_at: str - :param status: The status of this BioApiJobResponse. # noqa: E501 + :param status: The status of this NearestNeighborsResponse. # noqa: E501 :type status: BioApiStatus - :param result: The result of this BioApiJobResponse. # noqa: E501 + :param result: The result of this NearestNeighborsResponse. # noqa: E501 :type result: List[AnalysisResult] """ self.openapi_types = { @@ -58,27 +58,27 @@ def from_dict(cls, dikt): :param dikt: A dict. :type: dict - :return: The BioApiJobResponse of this BioApiJobResponse. # noqa: E501 - :rtype: BioApiJobResponse + :return: The NearestNeighborsResponse of this NearestNeighborsResponse. # noqa: E501 + :rtype: NearestNeighborsResponse """ return util.deserialize_model(dikt, cls) @property def job_id(self): - """Gets the job_id of this BioApiJobResponse. + """Gets the job_id of this NearestNeighborsResponse. - :return: The job_id of this BioApiJobResponse. + :return: The job_id of this NearestNeighborsResponse. :rtype: str """ return self._job_id @job_id.setter def job_id(self, job_id): - """Sets the job_id of this BioApiJobResponse. + """Sets the job_id of this NearestNeighborsResponse. - :param job_id: The job_id of this BioApiJobResponse. + :param job_id: The job_id of this NearestNeighborsResponse. :type job_id: str """ @@ -86,20 +86,20 @@ def job_id(self, job_id): @property def created_at(self): - """Gets the created_at of this BioApiJobResponse. + """Gets the created_at of this NearestNeighborsResponse. - :return: The created_at of this BioApiJobResponse. + :return: The created_at of this NearestNeighborsResponse. :rtype: str """ return self._created_at @created_at.setter def created_at(self, created_at): - """Sets the created_at of this BioApiJobResponse. + """Sets the created_at of this NearestNeighborsResponse. - :param created_at: The created_at of this BioApiJobResponse. + :param created_at: The created_at of this NearestNeighborsResponse. :type created_at: str """ @@ -107,20 +107,20 @@ def created_at(self, created_at): @property def status(self): - """Gets the status of this BioApiJobResponse. + """Gets the status of this NearestNeighborsResponse. - :return: The status of this BioApiJobResponse. + :return: The status of this NearestNeighborsResponse. :rtype: BioApiStatus """ return self._status @status.setter def status(self, status): - """Sets the status of this BioApiJobResponse. + """Sets the status of this NearestNeighborsResponse. - :param status: The status of this BioApiJobResponse. + :param status: The status of this NearestNeighborsResponse. :type status: BioApiStatus """ @@ -128,20 +128,20 @@ def status(self, status): @property def result(self): - """Gets the result of this BioApiJobResponse. + """Gets the result of this NearestNeighborsResponse. - :return: The result of this BioApiJobResponse. + :return: The result of this NearestNeighborsResponse. :rtype: List[AnalysisResult] """ return self._result @result.setter def result(self, result): - """Sets the result of this BioApiJobResponse. + """Sets the result of this NearestNeighborsResponse. - :param result: The result of this BioApiJobResponse. + :param result: The result of this NearestNeighborsResponse. :type result: List[AnalysisResult] """ diff --git a/web/src/SAP/generated/openapi/openapi.yaml b/web/src/SAP/generated/openapi/openapi.yaml index 5631d717..f492781f 100644 --- a/web/src/SAP/generated/openapi/openapi.yaml +++ b/web/src/SAP/generated/openapi/openapi.yaml @@ -356,7 +356,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BioApiJobResponse' + $ref: '#/components/schemas/NearestNeighborsResponse' description: Nearest Neighbors Response tags: - nearest_neighbors @@ -1288,7 +1288,7 @@ components: - unknownsAreDiffs title: NearestNeighborsRequest type: object - BioApiJobResponse: + NearestNeighborsResponse: example: result: - null diff --git a/web/src/SAP/generated/test/test_nearest_neighbors_controller.py b/web/src/SAP/generated/test/test_nearest_neighbors_controller.py index fef78cd3..3bf6e7c6 100644 --- a/web/src/SAP/generated/test/test_nearest_neighbors_controller.py +++ b/web/src/SAP/generated/test/test_nearest_neighbors_controller.py @@ -5,8 +5,8 @@ from flask import json from six import BytesIO -from web.src.SAP.generated.models.bio_api_job_response import BioApiJobResponse # noqa: E501 from web.src.SAP.generated.models.nearest_neighbors_request import NearestNeighborsRequest # noqa: E501 +from web.src.SAP.generated.models.nearest_neighbors_response import NearestNeighborsResponse # noqa: E501 from .test import BaseTestCase From 91f8649cbacf87c6ab7c66c51a2542589b336f94 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Tue, 21 May 2024 14:54:14 +0200 Subject: [PATCH 17/18] feat: loop selected rows --- .../nearest-neighbor-modal.tsx | 78 ++++++++++++++----- .../nearest-neighbor-query-configs.ts | 8 +- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx index 31feee9a..77e1dce8 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-modal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { NumberDecrementStepper, NumberIncrementStepper, @@ -44,41 +44,79 @@ export const NearestNeighborModal = (props: Props) => { const [unknownsAreDiffs, setUnknownsAreDiffs] = useState(true); const toast = useToast(); const dispatch = useDispatch(); + const [searchIndex, setSearchIndex] = useState(0); - const nearestNeighborsResponse = useSelector( + const nearestNeighborsResponses = useSelector( (state: { entities: NearestNeighborsResponseSlice }) => - state.entities.nearestNeighborsResponse + state.entities.nearestNeighborsResponses ); - const [{ isPending, status }, searchNearestNeighbors] = useMutation(() => { - const first = Object.values(selection)[0]; + const [{ isPending, status }, searchNearestNeighbors] = useMutation( + (index: number) => { + const first = Object.values(selection)[index]; - const req = { - id: first.original.id, - cutoff, - unknownsAreDiffs, - }; - return getNearestNeighbors(req); - }); + const req = { + id: first.original.id, + cutoff, + unknownsAreDiffs, + }; + return getNearestNeighbors(req); + } + ); const onSearch = useCallback(async () => { setIsSearching(true); - await searchNearestNeighbors(); + await searchNearestNeighbors(0); }, [setIsSearching, searchNearestNeighbors]); - React.useEffect(() => { + useEffect(() => { + if (searchIndex === 0) { + return; + } + searchNearestNeighbors(searchIndex); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [searchIndex]); + + useEffect(() => { if (isSearching && !isPending) { if (status >= 200 && status < 300) { + if (searchIndex + 1 !== Object.values(selection).length) { + setSearchIndex((prev) => prev + 1); + return; + } + + if ( + searchIndex !== 0 && + searchIndex + 1 == Object.values(selection).length && + Object.keys(nearestNeighborsResponses).length !== + Object.values(selection).length + ) { + // Wait for last response + return; + } + + const neighbors: Record = {}; + for (const sequenceId of Object.keys(selection)) { + const row = selection[sequenceId]; + const response = nearestNeighborsResponses[row.original.id]; + response.result?.forEach((neighbor) => { + // If not in selection, set as neighbor + if (!selection[neighbor.sequence_id]) { + neighbors[neighbor.sequence_id] = neighbor; + } + }); + } + toast({ - title: `Found ${nearestNeighborsResponse.result.length} neighbor(s)`, + title: `Found ${Object.keys(neighbors).length} neighbor(s)`, status: "success", duration: 5000, isClosable: true, }); - if (nearestNeighborsResponse.result) { + if (Object.values(neighbors).length) { const newSelection = Object.assign({}, selection); - nearestNeighborsResponse.result?.forEach((n) => { + Object.values(neighbors).forEach((n) => { newSelection[n.sequence_id] = { cells: {}, original: n, @@ -103,10 +141,11 @@ export const NearestNeighborModal = (props: Props) => { isSearching, isPending, status, - nearestNeighborsResponse, + nearestNeighborsResponses, onClose, dispatch, selection, + searchIndex, ]); return ( @@ -147,6 +186,9 @@ export const NearestNeighborModal = (props: Props) => { ) : ( <> +
+ {searchIndex} / {Object.values(selection).length} +
)} diff --git a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts index 3b86d3f9..cf42cc2d 100644 --- a/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts +++ b/app/src/app/analysis/nearest-neighbor/nearest-neighbor-query-configs.ts @@ -2,7 +2,7 @@ import { post, NearestNeighborsResponse, NearestNeighborsRequest } from "sap-cli import { getUrl } from "service"; export type NearestNeighborsResponseSlice = { - nearestNeighborsResponse: NearestNeighborsResponse; + nearestNeighborsResponses: Record; }; export const getNearestNeighbors = (params: NearestNeighborsRequest) => { @@ -10,13 +10,15 @@ export const getNearestNeighbors = (params: NearestNeighborsRequest) => { base.url = getUrl(base.url); base.transform = (response: NearestNeighborsResponse) => { + const resp = {}; + resp[params.id] = response; return { - nearestNeighborsResponse: response, + nearestNeighborsResponses: resp, }; }; base.update = { - nearestNeighborsResponse: (_, newValue) => newValue, + nearestNeighborsResponses: (oldValue, newValue) => Object.assign(newValue, oldValue ?? {}), }; // Force a network call to be made. Making it promise as well. From 7fa1001819fcb446d67e82a15458516c8fd591a1 Mon Sep 17 00:00:00 2001 From: Allan Hvam Date: Tue, 21 May 2024 15:07:12 +0200 Subject: [PATCH 18/18] feat: nn security trimming --- web/src/SAP/src/controllers/NearestNeighborsController.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/src/SAP/src/controllers/NearestNeighborsController.py b/web/src/SAP/src/controllers/NearestNeighborsController.py index 01500220..022c4466 100644 --- a/web/src/SAP/src/controllers/NearestNeighborsController.py +++ b/web/src/SAP/src/controllers/NearestNeighborsController.py @@ -39,9 +39,14 @@ def post(user, token, body: NearestNeighborsRequest): def get_result(id: str): row = get_single_analysis_by_object_id(id) + if ( + token["sofi-data-clearance"] == "own-institution" + and token["institution"] != row["institution"] + ): + return None return get_analysis_with_metadata(row["sequence_id"]) - result = list(map(lambda r : get_result(r["id"]), response_dict["result"])) + result = list(filter(lambda x: x is not None, list(map(lambda r : get_result(r["id"]), response_dict["result"])))) return jsonify({"status": api_response.status.value, "jobId": api_response.job_id,