From 0309ba2cf8a2f9b495ba4a7038504b93f35abac3 Mon Sep 17 00:00:00 2001 From: mwever Date: Fri, 7 Feb 2020 17:25:53 +0100 Subject: [PATCH 01/15] Added inline swagger-php annotations to bootstrap methods. --- openml_OS/models/api/v1/Api_data.php | 758 ++++++++++++++++++ .../models/api/v1/Api_estimationprocedure.php | 54 ++ openml_OS/models/api/v1/Api_evaluation.php | 93 +++ .../models/api/v1/Api_evaluationmeasure.php | 38 +- openml_OS/models/api/v1/Api_flow.php | 395 ++++++++- openml_OS/models/api/v1/Api_run.php | 588 +++++++++++++- openml_OS/models/api/v1/Api_setup.php | 292 +++++++ openml_OS/models/api/v1/Api_study.php | 315 +++++++- openml_OS/models/api/v1/Api_task.php | 442 +++++++++- openml_OS/models/api/v1/Api_tasktype.php | 141 ++++ openml_OS/models/api/v1/Api_user.php | 34 + 11 files changed, 3137 insertions(+), 13 deletions(-) diff --git a/openml_OS/models/api/v1/Api_data.php b/openml_OS/models/api/v1/Api_data.php index d21a55de1..493e00b46 100644 --- a/openml_OS/models/api/v1/Api_data.php +++ b/openml_OS/models/api/v1/Api_data.php @@ -31,23 +31,279 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); + + /** + *@OA\Get( + * path="/data/list/{filters}", + * tags={"data"}, + * summary="List and filter datasets", + * description="List datasets, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/data/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all datasets that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + /status/{status} - returns only datasets with a given status, either 'active', 'deactivated', or 'in_preparation'. + /tag/{tag} - returns only datasets tagged with the given tag. + /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, data_version, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'. + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of datasets with the given task", + * @OA\JsonContent( + * ref="#/components/schemas/DataList", + * example={ + * "data": { + * "dataset": [ + * { + * "did":"1", + * "name":"anneal", + * "status":"active", + * "format":"ARFF", + * "quality":[ + * { + * "name":"MajorityClassSize", + * "value":"684" + * }, + * { + * "name":"MaxNominalAttDistinctValues", + * "value":"10.0" + * }, + * { + * "name":"MinorityClassSize" + * ,"value":"0" + * }, + * { + * "name":"NumBinaryAtts", + * "value":"14.0" + * }, + * { + * "name":"NumberOfClasses", + * "value":"6" + * }, + * { + * "name":"NumberOfFeatures", + * "value":"39" + * }, + * { + * "name":"NumberOfInstances", + * "value":"898" + * }, + * { + * "name":"NumberOfInstancesWithMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfNumericFeatures", + * "value":"6" + * }, + * { + * "name":"NumberOfSymbolicFeatures", + * "value":"32" + * } + * ] + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n370 - Illegal filter specified.\n371 - Filter values/ranges not properly specified.\n372 - No results. There where no matches for the given constraints.\n373 - Can not specify an offset without a limit.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->data_list($segments); return; } + /** + *@OA\Get( + * path="/data/{id}", + * tags={"data"}, + * summary="Get dataset description", + * description="Returns information about a dataset. The information includes the name, information about the creator, URL to download it and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A dataset description", + * @OA\JsonContent( + * ref="#/components/schemas/Data", + * example={ + * "data_set_description": { + * "id": "1", + * "name": "anneal", + * "version": "2", + * "description": "...", + * "format": "ARFF", + * "upload_date": "2014-04-06 23:19:20", + * "licence": "Public", + * "url": "https://www.openml.org/data/download/1/dataset_1_anneal.arff", + * "file_id": "1", + * "default_target_attribute": "class", + * "version_label": "2", + * "tag": [ + * "study_1", + * "uci" + * ], + * "visibility": "public", + * "original_data_url": "https://www.openml.org/d/2", + * "status": "active", + * "md5_checksum": "d01f6ccd68c88b749b20bbe897de3713" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned\n110 - Please provide data_id.\n111 - Unknown dataset. Data set description with data_id was not found in the database.\n112 - No access granted. This dataset is not shared with you.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->data($segments[0]); return; } $order_values = array('random', 'normal'); + + /** + *@OA\Get( + * path="/data/unprocessed/{data_engine_id}/{order}", + * tags={"data"}, + * summary="Get a list of unprocessed datasets", + * description="This call is for people running their own dataset processing engines. It returns the details of datasets that are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", + * @OA\Parameter( + * name="data_engine_id", + * in="path", + * type="string", + * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", + * required="true", + * ), + * @OA\Parameter( + * name="order", + * in="path", + * type="string", + * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of unprocessed datasets", + * @OA\JsonContent( + * ref="#/components/schemas/DataUnprocessed", + * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n681 - No unprocessed datasets.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 3 && $segments[0] == 'unprocessed' && is_numeric($segments[1]) && in_array($segments[2], $order_values)) { $this->data_unprocessed($segments[1], $segments[2]); return; } + /** + *@OA\Post( + * path="/data/qualities/unprocessed/{data_engine_id}/{order}", + * tags={"data"}, + * summary="Get a list of datasets with unprocessed qualities", + * description="This call is for people running their own dataset processing engines. It returns the details of datasets for which certain qualities are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", + * @OA\Parameter( + * name="data_engine_id", + * in="path", + * type="string", + * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", + * required="true", + * ), + * @OA\Parameter( + * name="order", + * in="path", + * type="string", + * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Parameter( + * name="qualities", + * in="formData", + * type="string", + * description="Comma-separated list of (at least two) quality names, e.g. 'NumberOfInstances,NumberOfFeatures'.", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="A list of unprocessed datasets", + * @OA\JsonContent( + * ref="#/components/schemas/DataUnprocessed", + * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n686 - Please specify the features the evaluation engine wants to calculate (at least 2).\n687 - No unprocessed datasets according to the given set of meta-features.\n688 - Illegal qualities.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) >= 4 && count($segments) <= 6 && $segments[0] == 'qualities' && $segments[1] == 'unprocessed' && is_numeric($segments[2]) && in_array($segments[3], $order_values)) { $feature = (count($segments) > 4 && $segments[4] == 'feature'); // oops, badly defined api call with two optional parameters. boolean feature and string priority tag. @@ -64,6 +320,52 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Delete( + * path="/data/{id}", + * tags={"data"}, + * summary="Delete dataset", + * description="Deletes a dataset. Upon success, it returns the ID of the deleted dataset.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_delete", + * ref="#/components/schemas/inline_response_200_data_delete", + * ), + * example={ + * "data_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned\n- 350 - Please provide API key. In order to remove your content, please authenticate.\n- 351 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n- 352 - Dataset does not exists. The data ID could not be linked to an existing dataset.\n- 353 - Dataset is not owned by you. The dataset is owned by another user. Hence you cannot delete it.\n- 354 - Dataset is in use by other content. Can not be deleted. The data is used in tasks or runs. Delete other content before deleting this dataset.\n- 355 - Deleting dataset failed. Deleting the dataset failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->data_delete($segments[0]); return; @@ -74,26 +376,311 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/data", + * tags={"data"}, + * summary="Upload dataset", + * description="Uploads a dataset. Upon success, it returns the data id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/data).", + * required="true", + * ), + * @OA\Parameter( + * name="dataset", + * in="formData", + * type="file", + * description="The actual dataset, being an ARFF file.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_data_set", + * ref="#/components/schemas/inline_response_200_1_upload_data_set", + * ), + * example={ + * "upload_data_set": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n130 - Problem with file uploading. There was a problem with the file upload.\n131 - Problem validating uploaded description file. The XML description format does not meet the standards.\n132 - Failed to move the files. Internal server error, please contact API administrators.\n133 - Failed to make checksum of datafile. Internal server error, please contact API administrators.\n134 - Failed to insert record in database. Internal server error, please contact API administrators.\n135 - Please provide description xml.\n136 - File failed format verification. The uploaded file is not valid according to the selected file format. Please check the file format specification and try again.\n137 - Please provide API key. In order to share content, please log in or provide your API key.\n138 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n139 - Combination name / version already exists. Leave version out for auto increment\n140 - Both dataset file and dataset url provided. The system is confused since both a dataset file (post) and a dataset url (xml) are provided. Please remove one.\n141 - Neither dataset file or dataset url are provided. Please provide either a dataset file as POST variable, or a dataset url in the description XML.\n142 - Error in processing arff file. Can be a syntax error, or the specified target feature does not exists. For now, we only check on arff files. If a dataset is claimed to be in such a format, and it can not be parsed, this error is returned.\n143 - Suggested target feature not legal. It is possible to suggest a default target feature (for predictive tasks). However, it should be provided in the data.\n144 - Unable to update dataset. The dataset with id could not be found in the database. If you upload a new dataset, unset the id.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 0 && $request_type == 'post') { $this->data_upload(); return; } + /** + *@OA\Get( + * path="/data/features/{id}", + * tags={"data"}, + * summary="Get data features", + * description="Returns the features of a dataset.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="All the features of the dataset", + * @OA\JsonContent( + * ref="#/components/schemas/DataFeatures", + * example={ + * "data_features": { + * "feature": [ + * { + * "index": "0", + * "name": "sepallength", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "1", + * "name": "sepalwidth", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "2", + * "name": "petallength", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "3", + * "name": "petalwidth", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "4", + * "name": "class", + * "data_type": "nominal", + * "is_target": "true", + * "is_ignore": "false", + * "is_row_identifier": "false" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n270 - Please provide dataset ID.\n271 - Unknown dataset. Data set with the given data ID was not found (or is not shared with you).\n272 - No features found. The dataset did not contain any features, or we could not extract them.\n273 - Dataset not processed yet. The dataset was not processed yet, features are not yet available. Please wait for a few minutes.\n274 - Dataset processed with error. The feature extractor has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, please contact the API admins.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && $segments[0] == 'features' && is_numeric($segments[1]) && in_array($request_type, $getpost)) { $this->data_features($segments[1]); return; } + /** + *@OA\Post( + * path="/data/features", + * tags={"data"}, + * summary="Upload dataset feature description", + * description="Uploads dataset feature description. Upon success, it returns the data id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.features) and an [XML example](https://www.openml.org/api/v1/xml_example/data.features).", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n431 - Dataset already processed\n432 - Please provide description xml\n433 - Problem validating uploaded description file\n434 - Could not find dataset\n436 - Something wrong with XML, check data id and evaluation engine id\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'features' && $request_type == 'post') { $this->data_features_upload($segments[0]); return; } + /** + *@OA\Get( + * path="/data/qualities/list", + * tags={"data"}, + * summary="List all data qualities", + * description="Returns a list of all data qualities in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of data qualities", + * @OA\JsonContent( + * ref="#/components/schemas/DataQualityList", + * example={ + * "data_qualities_list":{ + * "quality":[ + * "NumberOfClasses", + * "NumberOfFeatures", + * "NumberOfInstances", + * "NumberOfInstancesWithMissingValues", + * "NumberOfMissingValues", + * "NumberOfNumericFeatures", + * "NumberOfSymbolicFeatures" + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned\n370 - No data qualities available. There are no data qualities in the system.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && $segments[0] == 'qualities' && $segments[1] == 'list' && in_array($request_type, $getpost)) { $this->data_qualities_list($segments[1]); return; } + /** + *@OA\Get( + * path="/data/qualities/{id}", + * tags={"data"}, + * summary="Get data qualities", + * description="Returns the qualities of a dataset.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="All the qualities of the dataset", + * @OA\JsonContent( + * ref="#/components/schemas/DataQualities", + * example={ + * "data_qualities": { + * "quality": [ + * { + * "name": "ClassCount", + * "value": "3.0" + * }, + * { + * "name": "ClassEntropy", + * "value": "1.584962500721156" + * }, + * { + * "name": "NumberOfClasses", + * "value": "3" + * }, + * { + * "name": "NumberOfFeatures", + * "value": "5" + * }, + * { + * "name": "NumberOfInstances", + * "value": "150" + * }, + * { + * "name": "NumberOfInstancesWithMissingValues", + * "value": "0" + * }, + * { + * "name": "NumberOfMissingValues", + * "value": "0" + * }, + * { + * "name": "NumberOfNumericFeatures", + * "value": "4" + * }, + * { + * "name": "NumberOfSymbolicFeatures", + * "value": "0" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n360 - Please provide data set ID\n361 - Unknown dataset. The data set with the given ID was not found in the database, or is not shared with you.\n362 - No qualities found. The registered dataset did not contain any calculated qualities.\n363 - Dataset not processed yet. The dataset was not processed yet, no qualities are available. Please wait for a few minutes.\n364 - Dataset processed with error. The quality calculator has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, contact the support team.\n365 - Interval start or end illegal. There was a problem with the interval\nstart or end.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && $segments[0] == 'qualities' && is_numeric($segments[1]) && in_array($request_type, $getpost)) { $this->data_qualities($segments[1], $this->config->item('default_evaluation_engine_id')); return; @@ -115,16 +702,150 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/data/qualities", + * tags={"data"}, + * summary="Upload dataset qualities", + * description="Uploads dataset qualities. Upon success, it returns the data id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.qualities) and an [XML example](https://www.openml.org/api/v1/xml_example/data.qualities).", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n381 - Something wrong with XML, please check did and evaluation_engine_id\n382 - Please provide description xml\n383 - Problem validating uploaded description file\n384 - Dataset not processed yet\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'qualities' && $request_type == 'post') { $this->data_qualities_upload($segments[0]); return; } + /** + *@OA\Post( + * path="/data/tag", + * tags={"data"}, + * summary="Tag a dataset", + * description="Tags a dataset.", + * @OA\Parameter( + * name="data_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_tag", + * ref="#/components/schemas/inline_response_200_2_data_tag", + * ), + * example={ + * "data_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { $this->entity_tag_untag('dataset', $this->input->post('data_id'), $this->input->post('tag'), false, 'data'); return; } + /** + *@OA\Post( + * path="/data/untag", + * tags={"data"}, + * summary="Untag a dataset", + * description="Untags a dataset.", + * @OA\Parameter( + * name="data_id", + * in="formData", + * type="number", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The ID of the untagged dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_untag", + * ref="#/components/schemas/inline_response_200_3_data_untag", + * ), + * example={ + * "data_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { $this->entity_tag_untag('dataset', $this->input->post('data_id'), $this->input->post('tag'), true, 'data'); return; @@ -135,6 +856,43 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/data/status/update/", + * tags={"data"}, + * summary="Change the status of a dataset", + * description="Change the status of a dataset, either 'active' or 'deactivated'", + * @OA\Parameter( + * name="data_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required="true", + * ), + * @OA\Parameter( + * name="status", + * in="formData", + * type="string", + * description="The status on which to filter the results, either 'active' or 'deactivated'.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n691 - Illegal status\n692 - Dataset does not exists\n693 - Dataset is not owned by you\n694 - Illegal status transition\n695 - Status update failed\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && $segments[0] == 'status' && $segments[1] == 'update') { $this->status_update($this->input->post('data_id'), $this->input->post('status')); return; diff --git a/openml_OS/models/api/v1/Api_estimationprocedure.php b/openml_OS/models/api/v1/Api_estimationprocedure.php index 3ad1709bc..3c5436d51 100644 --- a/openml_OS/models/api/v1/Api_estimationprocedure.php +++ b/openml_OS/models/api/v1/Api_estimationprocedure.php @@ -41,6 +41,60 @@ private function estimationprocedure($id) { } $this->xmlContents( 'estimationprocedure-get', $this->version, array( 'ep' => $ep ) ); } + + /** + *@OA\Get( + * path="/estimationprocedure/list", + * tags={"estimationprocedure"}, + * summary="List all estimation procedures", + * description="Returns an array with all model performance estimation procedures in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of estimation procedures", + * @OA\JsonContent( + * ref="#/components/schemas/EstimationProcedureList", + * example={ + * "estimationprocedures": { + * "estimationprocedure": [ + * { + * "id":"1", + * "ttid":"1", + * "name":"10-fold Crossvalidation", + * "type":"crossvalidation", + * "repeats":"1", + * "folds":"10", + * "stratified_sampling":"true" + * }, + * { + * "id":"2", + * "ttid":"1", + * "name":"5 times 2-fold Crossvalidation", + * "type":"crossvalidation", + * "repeats":"5", + * "folds":"2", + * "stratified_sampling":"true" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n500 - No model performance estimation procedures available.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function estimationprocedure_list() { $estimationprocedures = $this->Estimation_procedure->get(); diff --git a/openml_OS/models/api/v1/Api_evaluation.php b/openml_OS/models/api/v1/Api_evaluation.php index e5e79a117..faa3fb2a6 100644 --- a/openml_OS/models/api/v1/Api_evaluation.php +++ b/openml_OS/models/api/v1/Api_evaluation.php @@ -15,6 +15,54 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); + /** + *@OA\Get( + * path="/evaluation/list/{filters}", + * tags={"evaluation"}, + * summary="List and filter evaluations", + * description="List evaluations, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/evaluation/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, uploaders or runs.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /function/{name} - name of the evaluation measure, e.g. area_under_auc or predictive_accuracy. See the OpenML website for the complete list of measures. + /tag/{tag} - returns only evaluations of runs tagged with the given tag. + /run/{ids} - return only evaluations for specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3'' + /task/{ids} - return only evaluations for specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3'' + /flow/{ids} - return only evaluations for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' + /setup/{ids} - return only evaluations for specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' + /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + /per_fold/{true,false} - whether or not to return crossvalidation scores per fold. Defaults to 'false'. Setting it to 'true' leads to large numbers of results, use only for very specific sets of runs. + /sort_order/{asc,desc} - sorts the results by the evaluation value, according to the selected evaluation measure (function) + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of evaluations descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationList", + * example={"evaluations": {"evaluation": [{"function": "area_under_roc_curve", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]", "value": "0.839359", "uploader": "1", "flow_id": "61"}, {"function": "f_measure", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]", "value": "0.600026", "uploader": "1", "flow_id": "61"}, {"function": "predictive_accuracy", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": [], "value": "0.614634", "uploader": "1", "flow_id": "61"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n540 - Please provide at least task, flow or setup, uploader or run, to\nfilter results, or limit the number of responses.\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n542 - There where no results. Check whether there are runs under the given constraint.\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n544 - Illegal filter specified.\n545 - Offset specified without limit.\n546 - Requested result limit too high.\n547 - Per fold can only be set to value "true" or "false".\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->evaluation_list($segments, $user_id); @@ -22,6 +70,51 @@ function bootstrap($format, $segments, $request_type, $user_id) { } $order_values = array('random', 'reverse', 'normal'); + + /** + *@OA\Get( + * path="/evaluation/request/{evaluation_engine_id}/{order}", + * tags={"evaluation"}, + * summary="Get an unevaluated run", + * description="This call is for people running their own evaluation engines. It returns the details of a run that is not yet evaluated by the given evaluation engine. It doesn't evaluate the run, it just returns the run info.", + * @OA\Parameter( + * name="evaluation_engine_id", + * in="path", + * type="string", + * description="The ID of the evaluation engine. You get this ID when you register a new evaluation engine with OpenML. The ID of the main evaluation engine is 1.", + * required="true", + * ), + * @OA\Parameter( + * name="order", + * in="path", + * type="string", + * description="When there are multiple runs still to evaluate, this defines which one to return. Options are 'normal' - the oldest run, 'reverse' - the newest run, or 'random' - a random run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of evaluations descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationRequest", + * example={"evaluation_request": {"run": [{"setup_id": "68799271", "upload_time": "2018-04-03 21:05:38", "uploader": "1935", "task_id": "3021", "run_id": "8943712"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n100 - Function not valid.\n545 - No unevaluated runs according to the criteria.\n546 - Illegal filter.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) >= 4 && $segments[0] == 'request' && is_numeric($segments[1]) && in_array($segments[2], $order_values) && is_numeric($segments[3])) { array_shift($segments); // removes 'request' $eval_id = array_shift($segments); diff --git a/openml_OS/models/api/v1/Api_evaluationmeasure.php b/openml_OS/models/api/v1/Api_evaluationmeasure.php index 7cde44a37..1aa33afc3 100644 --- a/openml_OS/models/api/v1/Api_evaluationmeasure.php +++ b/openml_OS/models/api/v1/Api_evaluationmeasure.php @@ -23,8 +23,42 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } - - + + + /** + *@OA\Get( + * path="/evaluationmeasure/list", + * tags={"evaluationmeasure"}, + * summary="List all evaluation measures", + * description="Returns an array with all model evaluation measures in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of evaluation measures", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationMeasureList", + * example={ + * "evaluation_measures":{ + * "measures":{ + * "measure":[ + * "area_under_roc_curve", + * "average_cost", + * "binominal_test", + * "build_cpu_time" + * ] + * } + * } + * } + * ), + * ), + *) + */ private function evaluationmeasure_list() { $data = new stdClass(); $data->measures = $this->Math_function->getWhere( 'functionType = "EvaluationFunction"' ); diff --git a/openml_OS/models/api/v1/Api_flow.php b/openml_OS/models/api/v1/Api_flow.php index d570ea3c8..9616bece4 100644 --- a/openml_OS/models/api/v1/Api_flow.php +++ b/openml_OS/models/api/v1/Api_flow.php @@ -44,11 +44,121 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Get( + * path="/flow/{id}", + * tags={"flow"}, + * summary="Get flow description", + * description="Returns information about a flow. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the flow.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A flow description", + * @OA\JsonContent( + * ref="#/components/schemas/Flow", + * example={ + * "flow": { + * "id":"100", + * "uploader":"1", + * "name":"weka.J48", + * "version":"2", + * "external_version":"Weka_3.7.5_9117", + * "description":"...", + * "upload_date":"2014-04-23 18:00:36", + * "language":"Java", + * "dependencies":"Weka_3.7.5", + * "parameter": [ + * { + * "name":"A", + * "data_type":"flag", + * "default_value":[], + * "description":"Laplace smoothing..." + * }, + * { + * "name":"C", + * "data_type":"option", + * "default_value":"0.25", + * "description":"Set confidence threshold..." + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n180 - Please provide flow id.\n181 - Unknown flow. The flow with the given ID was not found in the database.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->flow($segments[0]); return; } + /** + *@OA\Delete( + * path="/flow/{id}", + * tags={"flow"}, + * summary="Delete a flow", + * description="Deletes a flow. Upon success, it returns the ID of the deleted flow.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the flow.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_delete", + * ref="#/components/schemas/inline_response_200_8_flow_delete", + * ), + * example={ + * "flow_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n320 - Please provide API key. In order to remove your content, please authenticate.\n321 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n322 - Flow does not exists. The flow ID could not be linked to an existing flow.\n323 - Flow is not owned by you. The flow is owned by another user. Hence you cannot delete it.\n324 - Flow is in use by other content. Can not be deleted. The flow is used in runs, evaluations or as a component of another flow. Delete other content before deleting this flow.\n325 - Deleting flow failed. Deleting the flow failed. Please contact\nsupport team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->flow_delete($segments[0]); return; @@ -64,11 +174,116 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/flow/tag", + * tags={"flow"}, + * summary="Tag a flow", + * description="Tags a flow.", + * @OA\Parameter( + * name="flow_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the flow.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_tag", + * ref="#/components/schemas/inline_response_200_12_flow_tag", + * ), + * example={ + * "flow_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { $this->entity_tag_untag('implementation', $this->input->post('flow_id'), $this->input->post('tag'), false, 'flow'); return; } + /** + *@OA\Post( + * path="/flow/untag", + * tags={"flow"}, + * summary="Untag a flow", + * description="Untags a flow.", + * @OA\Parameter( + * name="flow_id", + * in="formData", + * type="number", + * description="Id of the flow.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the untagged flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_untag", + * ref="#/components/schemas/inline_response_200_13_flow_untag", + * ), + * example={ + * "flow_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { $this->entity_tag_untag('implementation', $this->input->post('flow_id'), $this->input->post('tag'), true, 'flow'); return; @@ -82,7 +297,77 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } - + /** + *@OA\Get( + * path="/flow/list/{filters}", + * tags={"flow"}, + * summary="List and filter flows", + * description="List flows, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all flows that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. + /tag/{tag} - returns only tasks tagged with the given tag. + /uploader/{id} - return only evaluations uploaded by a specific user, specified by user ID. + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of flows", + * @OA\JsonContent( + * ref="#/components/schemas/FlowList", + * example={ + * "flows": + * { + * "flow":[ + * { + * "id":"65", + * "full_name":"weka.RandomForest(1)", + * "name":"weka.RandomForest", + * "version":"1", + * "external_version":"Weka_3.7.10_9186", + * "uploader":"1" + * }, + * { + * "id":"66", + * "full_name":"weka.IBk(1)", + * "name":"weka.IBk", + * "version":"1", + * "external_version":"Weka_3.7.10_8034", + * "uploader":"1" + * }, + * { + * "id":"67", + * "full_name":"weka.BayesNet_K2(1)", + * "name":"weka.BayesNet_K2", + * "version":"1", + * "external_version":"Weka_3.7.10_8034", + * "uploader":"1" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n500 - No results. There where no matches for the given constraints.\n501 - Illegal filter specified.\n502 - Filter values/ranges not properly specified.\n503 - Can not specify an offset without a limit.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function flow_list($segs) { $legal_filters = array('uploader', 'tag', 'limit', 'offset'); @@ -137,6 +422,59 @@ private function flow_list($segs) { } + /** + *@OA\Get( + * path="/flow/exists/{name}/{version}", + * tags={"flow"}, + * summary="Check whether flow exists", + * description="Checks whether a flow with the given name and (external) version exists.", + * @OA\Parameter( + * name="name", + * in="path", + * type="string", + * description="The name of the flow.", + * required="true", + * ), + * @OA\Parameter( + * name="version", + * in="path", + * type="string", + * description="The external version of the flow", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of flows", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_exists", + * ref="#/components/schemas/inline_response_200_10_flow_exists", + * ), + * example={ + * "flow_exists": { + * "exists": "true", + * "id": "65" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n330 - Mandatory fields not present. Please provide name and external_version.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function flow_exists($name, $external_version) { $similar = false; @@ -153,8 +491,59 @@ private function flow_exists($name, $external_version) { } $this->xmlContents( 'implementation-exists', $this->version, $result ); } - - // TODO: check what is going wrong with implementation id 1 + /** + *@OA\Post( + * path="/flow", + * tags={"flow"}, + * summary="Upload a flow", + * description="Uploads a flow. Upon success, it returns the flow id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", + * required="true", + * ), + * @OA\Parameter( + * name="flow", + * in="formData", + * type="file", + * description="The actual flow, being a source (or binary) file.", + * required="false", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_9_upload_flow", + * ), + * example={ + * "upload_flow": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n160 - Error in file uploading. There was a problem with the file upload.\n161 - Please provide description xml.\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\n164 - Flow already stored in database. Please change name or version number\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\n166 - Failed to add flow to database. Internal server error, please contact API administrators\n167 - Illegal files uploaded. An non required file was uploaded.\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n171 - Flow already exists. This flow is already in the database\n172 - XSD not found. Please contact API support team\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + // TODO: check what is going wrong with implementation id 1 private function flow($id) { if( $id == false ) { $this->returnError( 180, $this->version ); diff --git a/openml_OS/models/api/v1/Api_run.php b/openml_OS/models/api/v1/Api_run.php index 34348c8e2..9940b0104 100644 --- a/openml_OS/models/api/v1/Api_run.php +++ b/openml_OS/models/api/v1/Api_run.php @@ -48,6 +48,51 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/run/evaluate", + * tags={"run"}, + * summary="Uploads a run evaluation", + * description="Uploads a run evaluation. When successful, it returns the run id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the run evaluation.Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.evaluate) and an [XML example](https://www.openml.org/api/v1/xml_example/run.evaluate).", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the evaluated run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_21_upload_flow", + * ), + * example={ + * "run_evaluate": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n422 - Upload problem description XML\n423 - Problem validating uploaded description file\n424 - Problem opening description xml\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'evaluate' && $request_type == 'post') { $this->run_evaluate(); return; @@ -83,11 +128,116 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/run/tag", + * tags={"run"}, + * summary="Tag a run", + * description="Tags a run.", + * @OA\Parameter( + * name="run_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_tag", + * ref="#/components/schemas/inline_response_200_19_run_tag", + * ), + * example={ + * "run_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { $this->entity_tag_untag('run', $this->input->post('run_id'), $this->input->post('tag'), false, 'run'); return; } + /** + *@OA\Post( + * path="/run/untag", + * tags={"run"}, + * summary="Untag a run", + * description="Untags a run.", + * @OA\Parameter( + * name="run_id", + * in="formData", + * type="number", + * description="Id of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the untagged run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_untag", + * ref="#/components/schemas/inline_response_200_20_run_untag", + * ), + * example={ + * "run_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { $this->entity_tag_untag('run', $this->input->post('run_id'), $this->input->post('tag'), true, 'run'); return; @@ -101,7 +251,51 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } - + /** + *@OA\Get( + * path="/run/list/{filters}", + * tags={"run"}, + * summary="List and filter runs", + * description="List runs, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/run/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all runs that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, or uploaders.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /tag/{tag} - return only runs tagged with the given tag. + /run/{ids} - return only specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3'' + /task/{ids} - return only runs on specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3'' + /flow/{ids} - return only runs on specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' + /setup/{ids} - return only runs with specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' + /uploader/{ids} - return only runs uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of runs descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/RunList", + * example={"runs": {"run": [{"upload_time": "2014-04-06 23:30:40", "task_id": "28", "run_id": "100", "error_message": [], "setup_id": "12", "uploader": "1", "flow_id": "67"}, {"upload_time": "2014-04-06 23:30:40", "task_id": "48", "run_id": "101", "error_message": [], "setup_id": "6", "uploader": "1", "flow_id": "61"}, {"upload_time": "2014-04-06 23:30:40", "task_id": "41", "run_id": "102", "error_message": [], "setup_id": "3", "uploader": "1", "flow_id": "58"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n510 - Please provide at least task, flow or setup, uploader or run, to filter results, or limit the number of responses. The number of runs is huge. Please limit the result space.\n511 - Input not safe. The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n512 - There where no results. Check whether there are runs under the given constraint.\n513 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n514 - Illegal filter specified.\n515 - Offset specified without limit.\n516 - Requested result limit too high.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_list($segs, $user_id) { $result_limit = 10000; $legal_filters = array('task', 'setup', 'flow', 'uploader', 'run', 'tag', 'limit', 'offset', 'task_type', 'study', 'show_errors'); @@ -203,7 +397,124 @@ private function run_list($segs, $user_id) { $this->xmlContents('runs', $this->version, array('runs' => $res)); } - + /** + *@OA\Get( + * path="/run/{id}", + * tags={"run"}, + * summary="Get run description", + * description="Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A run description", + * @OA\JsonContent( + * ref="#/components/schemas/Run", + * example={ + * "run": { + * "run_id":"100", + * "uploader":"1", + * "uploader_name":"Jan van Rijn", + * "task_id":"28", + * "task_type":"Supervised Classification", + * "task_evaluation_measure":"predictive_accuracy", + * "flow_id":"67", + * "flow_name":"weka.BayesNet_K2(1)", + * "setup_string":"weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", + * "parameter_setting": [ + * { + * "name":"D", + * "value":"true" + * }, + * { + * "name":"Q", + * "value":"weka.classifiers.bayes.net.search.local.K2" + * }, + * { + * "name":"P", + * "value":"1" + * }, + * { + * "name":"S", + * "value":"BAYES" + * } + * ], + * "input_data": + * { + * "dataset": + * { + * "did":"28", + * "name":"optdigits", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/28\\/dataset_28_optdigits.arff" + * } + * }, + * "output_data": + * { + * "file": [ + * { + * "did":"48838", + * "file_id":"261", + * "name":"description", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/261\\/weka_generated_run935374685998857626.xml" + * }, + * { + * "did":"48839", + * "file_id":"262", + * "name":"predictions", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff" + * } + * ], + * "evaluation": [ + * { + * "name":"area_under_roc_curve", + * "flow_id":"4", + * "value":"0.990288", + * "array_data":"[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]" + * }, + * { + * "name":"confusion_matrix", + * "flow_id":"10", + * "array_data":"[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]" + * }, + * { + * "name":"f_measure", + * "flow_id":"12", + * "value":"0.922723", + * "array_data":"[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]" + * }, + * { + * "name":"kappa", + * "flow_id":"13", + * "value":"0.913601" + * } + * ] + * } + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n220 - Please provide run ID. In order to view run details, please provide the run ID.\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run($run_id) { if( $run_id == false ) { $this->returnError( 235, $this->version ); @@ -229,7 +540,52 @@ private function run($run_id) { $this->xmlContents( 'run-get', $this->version, array( 'source' => $run ) ); } - + /** + *@OA\Delete( + * path="/run/{id}", + * tags={"run"}, + * summary="Delete run", + * description="Deletes a run. Upon success, it returns the ID of the deleted run.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_delete", + * ref="#/components/schemas/inline_response_200_17_data_delete", + * ), + * example={ + * "run_delete": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n390 - Please provide API key. In order to remove your content, please authenticate.\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\n392 - Run does not exists. The run ID could not be linked to an existing run.\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_delete($run_id) { $run = $this->Run->getById( $run_id ); @@ -280,6 +636,51 @@ private function run_delete($run_id) { } + /** + *@OA\Get( + * path="/run/reset/{id}", + * tags={"run"}, + * summary="Resets a run.", + * description="Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.", + * @OA\Parameter( + * name="id", + * in="path", + * type="string", + * description="Run ID.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="Id of the evaluated run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_reset", + * ref="#/components/schemas/inline_response_200_21_upload_flow", + * ), + * example={ + * "run_reset": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n412 - Run does not exist\n413 - Run is not owned by you\n394 - Resetting run failed\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_reset($run_id) { $run = $this->Run->getById( $run_id ); @@ -302,6 +703,72 @@ private function run_reset($run_id) { $this->xmlContents( 'run-reset', $this->version, array( 'run' => $run ) ); } + /** + *@OA\Post( + * path="/run", + * tags={"run"}, + * summary="Upload run", + * description="Uploads a run. Upon success, it returns the run id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", + * required="true", + * ), + * @OA\Parameter( + * name="predictions", + * in="formData", + * type="file", + * description="The predictions generated by the run", + * required="true", + * ), + * @OA\Parameter( + * name="model_readable", + * in="formData", + * type="file", + * description="The human-readable model generated by the run", + * required="false", + * ), + * @OA\Parameter( + * name="model_serialized", + * in="formData", + * type="file", + * description="The serialized model generated by the run", + * required="false", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_18_upload_flow", + * ), + * example={ + * "upload_run": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n202 - Please provide run XML.\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\n204 - Unknown task. The task with the given ID was not found in the database.\n205 - Unknown flow. The flow with the given ID was not found in the database.\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\n207 - File upload failed. One of the files uploaded has a problem.\n208 - Error inserting setup record. Please contact api administrators\n210 - Unable to store run. Please contact api administrators.\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\n212 - Unable to store file. Please contact api administrators.\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\n214 - Unable to store input setting. Please contact API support team.\n215 - Unable to evaluate predictions. Please contact API support team.\n216 - Error thrown by Java Application. Additional information field is provided.\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\n219 - Error reading the XML document. The XML description file could not be verified.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_upload() { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -604,6 +1071,68 @@ private function run_upload() { $this->xmlContents( 'run-upload', $this->version, $result ); } + /** + *@OA\Get( + * path="/run/trace/{id}", + * tags={"run"}, + * summary="Get run trace", + * description="Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A run trace description", + * @OA\JsonContent( + * ref="#/components/schemas/RunTrace", + * example={ + * "trace": { + * "run_id":"573055", + * "trace_iteration": { + * "repeat":"0", + * "fold":"0", + * "repeat":"0", + * "iteration":"0", + * "setup_string":{"parameter_minNumObj": "1", + * "parameter_confidenceFactor": "0.1"}, + * "evaluation":"94.814815", + * "selected": "true" + * }, + * "trace_iteration": { + * "repeat":"0", + * "fold":"0", + * "repeat":"0", + * "iteration":"1", + * "setup_string":{"parameter_minNumObj": "1", + * "parameter_confidenceFactor": "0.25"}, + * "evaluation": "94.074074", + * "selected": "true" + * } + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n570 - No successful trace associated with this run\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_trace($run_id) { $run = $this->Run->getById($run_id); if ($run === false) { @@ -620,6 +1149,59 @@ private function run_trace($run_id) { $this->xmlContents('run-trace-get', $this->version, array('run_id' => $run_id, 'trace' => $trace)); } + /** + *@OA\Post( + * path="/run/trace/{id}", + * tags={"run"}, + * summary="Upload run trace", + * description="Uploads a run trace. Upon success, it returns the run id.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the run with the trace", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_23_upload_flow", + * ), + * example={ + * "run_trace": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n561 - Problem with uploaded trace file.\n562 - Problem validating xml trace file.\n563 - Problem loading xml trace file.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_trace_upload() { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); diff --git a/openml_OS/models/api/v1/Api_setup.php b/openml_OS/models/api/v1/Api_setup.php index 798c2eb11..599441ca5 100644 --- a/openml_OS/models/api/v1/Api_setup.php +++ b/openml_OS/models/api/v1/Api_setup.php @@ -24,27 +24,319 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); + /** + *@OA\Get( + * path="/setup/{id}", + * tags={"setup"}, + * summary="Get a hyperparameter setup", + * description="Returns information about a setup. The information includes the list of hyperparameters, with name, value, and default value.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A setup description", + * @OA\JsonContent( + * ref="#/components/schemas/Setup", + * example={ + * "setup_parameters":{ + * "flow_id":"59", + * "parameter":[ + * { + * "full_name":"weka.JRip(1)_F", + * "parameter_name":"F", + * "data_type":"option", + * "default_value":"3", + * "value":"3" + * },{ + * "full_name":"weka.JRip(1)_N", + * "parameter_name":"N", + * "data_type":"option", + * "default_value":"2.0", + * "value":"2.0" + * },{ + * "full_name":"weka.JRip(1)_O", + * "parameter_name":"O", + * "data_type":"option", + * "default_value":"2", + * "value":"2" + * },{ + * "full_name":"weka.JRip(1)_S", + * "parameter_name":"S", + * "data_type":"option", + * "default_value":"1", + * "value":"1" + * }] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n280 - Please provide setup ID. In order to view setup details, please provide the run ID\n281 - Setup not found. The setup ID was invalid, or setup does not exist (anymore).\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->setup($segments[0]); return; } + /** + *@OA\Get( + * path="/setup/list/{filters}", + * tags={"setup"}, + * summary="List and filter setups", + * description="List setups, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/setup/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 1,000 results are returned at a time, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain flows, setups, or tags.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /tag/{tag} - returns only setups tagged with the given tag. + /flow/{ids} - return only setups for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' + /setup/{ids} - return only specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of setup descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/SetupList", + * example={ + * "setups": { + * "setup": [ + * { + * "setup_id":"10", + * "flow_id":"65", + * "parameter": [ + * { + * "id":"4144", + * "flow_id":"65", + * "flow_name":"weka.RandomForest", + * "full_name":"weka.RandomForest(1)_I", + * "parameter_name":"I", + * "data_type":"option", + * "default_value":"10", + * "value":"10" + * }, + * { + * "id":"4145", + * "flow_id":"65", + * "flow_name":"weka.RandomForest", + * "full_name":"weka.RandomForest(1)_K", + * "parameter_name":"K", + * "data_type":"option", + * "default_value":"0", + * "value":"0" + * } + * ] + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n670 - Please specify at least one filter.\n671 - Illegal filter.\n672 - Illegal filter input.\n673 - Result set too big. Please use one of the filters or the limit option.\n674 - No results, please check the filter.\n675 - Cannot specify offset without limit.\n676 - Requested result limit too high.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->setup_list($segments); return; } + /** + *@OA\Delete( + * path="/setup/{id}", + * tags={"setup"}, + * summary="Delete setup", + * description="Deletes a setup. Upon success, it returns the ID of the deleted setup.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the setup.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted setup", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_delete", + * ref="#/components/schemas/inline_response_200_14_study_delete", + * ), + * example={ + * "setup_delete": { + * "id": "1" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n401 - Authentication failed. Please provide API key. In order to remove your content, please authenticate.\n402 - Setup does not exists. The setup ID could not be linked to an existing setup.\n404 - Setup deletion failed. Setup is in use by other content (runs, schedules, etc). Can not be deleted.\n405 - Setup deletion failed. Please try again later.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->setup_delete($segments[0]); return; } + /** + *@OA\Post( + * path="/setup/tag", + * tags={"setup"}, + * summary="Tag a setup", + * description="Tags a setup.", + * @OA\Parameter( + * name="setup_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the setup.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged setup", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_tag", + * ref="#/components/schemas/inline_response_200_15_flow_tag", + * ), + * example={ + * "setup_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { $this->entity_tag_untag('algorithm_setup', $this->input->post('setup_id'), $this->input->post('tag'), false, 'setup'); return; } + /** + *@OA\Post( + * path="/setup/untag", + * tags={"setup"}, + * summary="Untag a setup", + * description="Untags a setup.", + * @OA\Parameter( + * name="setup_id", + * in="formData", + * type="number", + * description="Id of the setup.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the untagged setup", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_untag", + * ref="#/components/schemas/inline_response_200_16_flow_untag", + * ), + * example={ + * "setup_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { $this->entity_tag_untag('algorithm_setup', $this->input->post('setup_id'), $this->input->post('tag'), true, 'setup'); return; diff --git a/openml_OS/models/api/v1/Api_study.php b/openml_OS/models/api/v1/Api_study.php index 8bb45dfb6..b623abf3d 100644 --- a/openml_OS/models/api/v1/Api_study.php +++ b/openml_OS/models/api/v1/Api_study.php @@ -16,12 +16,117 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); + /** + *@OA\Get( + * path="/study/list/{filters}", + * tags={"study"}, + * summary="List all studies (collections of items)", + * description="List studies, optionally filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/study/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all studies that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /main_entity_type/{type} - only return studies collecting entities of a given type (e.g. 'task' or 'run'). + /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of studies", + * @OA\JsonContent( + * ref="#/components/schemas/StudyList", + * example={ + * "study_list":{ + * "study":[ + * { + * "id":"1", + * "alias":"Study_1", + * "name":"A large-scale comparison of classification algorithms", + * "creation_date":"2017-07-20 15:51:20", + * "creator":"2" + * }, + * { + * "id":"2", + * "alias":"Study_2", + * "name":"Fast Algorithm Selection using Learning Curves", + * "creation_date":"2017-07-20 15:51:20", + * "creator":"2" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) > 0 && $segments[0] == 'list') { array_shift($segments); $this->study_list($segments); return; } - + + /** + *@OA\Post( + * path="/study", + * tags={"study"}, + * summary="Create new study", + * description="Creates a new study. Upon success, it returns the study id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the study. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.study.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/study).", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_study", + * ref="#/components/schemas/inline_response_200_25_upload_study", + * ), + * example={ + * "upload_study": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1031 - Description file not present. Please upload the study description.\n1032 - Problem validating uploaded description file. The XML description format does not meet the standards. See the XSD schema.\n1033 - Illegal main entity type. Currently only collections of tasks and can be created.\n1034 - Linked entities are not of the correct type fot this study.\n1035 - Benchmark suites can only be linked to run studies.\n1036 - Referred benchmark suite cannot be found.\n1037 - Referred benchmark suite should be a task collection.\n1038 - Study alias is not unique.\n1039 - Dataset insertion problem. Please contact the administrators.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 0) { $this->study_create(); return; @@ -31,7 +136,53 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->status_update($this->input->post('study_id'), $this->input->post('status')); return; } - + + /** + *@OA\Delete( + * path="/study/{id}", + * tags={"study"}, + * summary="Delete study", + * description="Deletes a study. Upon success, it returns the ID of the deleted study.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_delete", + * ref="#/components/schemas/inline_response_200_24_study_delete", + * ), + * example={ + * "study_delete": { + * "id": "1" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n591 - Please provide API key. In order to remove your content, please authenticate.\n592 - Study does not exists. The study ID could not be linked to an existing study.\n593 - Study deletion failed. Please try again later.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->study_delete($segments[0]); return; @@ -53,6 +204,54 @@ function bootstrap($format, $segments, $request_type, $user_id) { $type = $segments[1]; } + /** + *@OA\Get( + * path="/study/{id}", + * tags={"study"}, + * summary="Get study description by study id or alias", + * description="Returns information about the study with the given id or alias.", + * @OA\Parameter( + * name="id", + * in="path", + * type="string", + * description="ID or alias of the study.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A study description", + * @OA\JsonContent( + * ref="#/components/schemas/Study", + * example={ + * "study": { + * "id": "99", + * "main_entity_type": "task", + * "name": "CC18 benchmark suite", + * "description": "CC18 benchmark suite", + * "creation_date": "2019-02-16T17:35:58", + * "creator": "1159", + * "data": {"data_id": ["1","2","3"]}, + * "tasks": {"task_id": ["1","2","3"]} + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n601 - Unknown study. The study with the given id or alias was not found in the database\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (is_numeric($segments[0])) { $this->study_by_id($segments[0], $type); return; @@ -206,7 +405,117 @@ private function status_update($study_id, $status) { $this->xmlContents('study-status-update', $this->version, $template_vars); } - + + /** + *@OA\Post( + * path="/study/{id}/attach", + * tags={"study"}, + * summary="Attach a new entity to a study", + * description="Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study. Supplied in the URL path.", + * required="true", + * ), + * @OA\Parameter( + * name="ids", + * in="formData", + * type="string", + * description="Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Properties of the updated study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_attach", + * ref="#/components/schemas/inline_response_200_26_study_attach", + * ), + * example={ + * "study_attach": { + * "id": "1", + * "main_entity_type": "task", + * "linked_entities": "5" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannnot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + /** + *@OA\Post( + * path="/study/{id}/detach", + * tags={"study"}, + * summary="Detach an entity from a study", + * description="Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study.", + * required="true", + * ), + * @OA\Parameter( + * name="ids", + * in="formData", + * type="string", + * description="Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Properties of the updated study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_study", + * ref="#/components/schemas/inline_response_200_26_study_attach", + * ), + * example={ + * "study_detach": { + * "id": "1", + * "main_entity_type": "task", + * "linked_entities": "5" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function study_attach_detach($study_id, $attach) { $study = $this->Study->getById($study_id); if ($study === false) { diff --git a/openml_OS/models/api/v1/Api_task.php b/openml_OS/models/api/v1/Api_task.php index acf37cb2a..725cf0556 100644 --- a/openml_OS/models/api/v1/Api_task.php +++ b/openml_OS/models/api/v1/Api_task.php @@ -26,7 +26,134 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->outputFormat = $format; $getpost = array('get','post'); - + /** + *@OA\Get( + * path="/task/list/{filters}", + * tags={"task"}, + * summary="List and filter tasks", + * description="List tasks, possibly filtered by a range of properties from the task itself or from the underlying dataset. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all tasks that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. + /status/{status} - returns only tasks with a given status, either 'active', 'deactivated', or 'in_preparation'. + /type/{type_id} - returns only tasks with a given task type id. See the list of task types of the ID's (e.g. 1 = Supervised Classification). + /tag/{tag} - returns only tasks tagged with the given tag. + /data_tag/{tag} - returns only tasks for which the underlying dataset is tagged with the given tag. + /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'. + ", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of tasks with the given tag", + * @OA\JsonContent( + * ref="#/components/schemas/TaskList", + * example={ + * "task": { + * "task": [ + * { + * "task_id":"1", + * "task_type":"Supervised Classification", + * "did":"1", + * "name":"anneal", + * "status":"active", + * "format":"ARFF", + * "input":[ + * { + * "name":"estimation_procedure", + * "value":"1" + * }, + * { + * "name":"evaluation_measures", + * "value":"predictive_accuracy" + * }, + * { + * "name":"source_data", + * "value":"1" + * }, + * { + * "name":"target_feature", + * "value":"class" + * } + * ], + * "quality":[ + * { + * "name":"MajorityClassSize", + * "value":"684" + * }, + * { + * "name":"MaxNominalAttDistinctValues", + * "value":"10.0" + * }, + * { + * "name":"MinorityClassSize", + * "value":"0" + * }, + * { + * "name":"NumBinaryAtts", + * "value":"14.0" + * }, + * { + * "name":"NumberOfClasses", + * "value":"6" + * }, + * { + * "name":"NumberOfFeatures", + * "value":"39" + * }, + * { + * "name":"NumberOfInstances", + * "value":"898" + * }, + * { + * "name":"NumberOfInstancesWithMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfNumericFeatures", + * "value":"6" + * }, + * { + * "name":"NumberOfSymbolicFeatures", + * "value":"32" + * } + * ], + * "tag":[ + * "basic", + * "study_1", + * "study_7", + * "under100k", + * "under1m" + * ] + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n480 - Illegal filter specified.\n481 - Filter values/ranges not properly specified.\n482 - No results. There where no matches for the given constraints.\n483 - Can not specify an offset without a limit.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->task_list($segments, $user_id); @@ -43,6 +170,51 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/task", + * tags={"task"}, + * summary="Upload task", + * description="Uploads a task. Upon success, it returns the task id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the task. Only name, description, and task format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.task.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/task).", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_task", + * ref="#/components/schemas/inline_response_200_5_upload_task", + * ), + * example={ + * "upload_task": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n530 - Description file not present. Please upload the task description.\n531 - Internal error. Please contact api support team\n532 - Problem validating uploaded description file. The XML description format does not meet the standards\n533 - Task already exists.\n534 - Error creating the task.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 0 && $request_type == 'post') { $this->task_upload(); return; @@ -53,11 +225,116 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/task/tag", + * tags={"task"}, + * summary="Tag a task", + * description="Tags a task.", + * @OA\Parameter( + * name="task_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_tag", + * ref="#/components/schemas/inline_response_200_6_task_tag", + * ), + * example={ + * "task_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, task, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { $this->entity_tag_untag('task', $this->input->post('task_id'), $this->input->post('tag'), false, 'task'); return; } + /** + *@OA\Post( + * path="/task/untag", + * tags={"task"}, + * summary="Untag a task", + * description="Untags a task.", + * @OA\Parameter( + * name="task_id", + * in="formData", + * type="number", + * description="Id of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="A the features of the task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_untag", + * ref="#/components/schemas/inline_response_200_7_task_untag", + * ), + * example={ + * "task_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, task, flow, run} not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, task, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { $this->entity_tag_untag('task', $this->input->post('task_id'), $this->input->post('tag'), true, 'task'); return; @@ -149,7 +426,122 @@ private function task_list($segs, $user_id) { $this->xmlContents( 'tasks', $this->version, array( 'tasks' => $tasks_res ) ); } - + /** + *@OA\Get( + * path="/task/{id}", + * tags={"task"}, + * summary="Get task description", + * description="Returns information about a task. The information includes the task type, input data, train/test sets, and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A task description", + * @OA\JsonContent( + * ref="#/components/schemas/Task", + * example={ + * "task": { + * "task_id":"1", + * "task_type":"Supervised Classification", + * "input":[ + * { + * "name":"source_data", + * "data_set":{ + * "data_set_id":"1", + * "target_feature":"class" + * } + * }, + * { + * "name":"estimation_procedure", + * "estimation_procedure":{ + * "type":"crossvalidation", + * "data_splits_url":"https://www.openml.org/api_splits/get/1/Task_1_splits.arff", + * "parameter":[ + * { + * "name":"number_repeats", + * "value":"1" + * }, + * { + * "name":"number_folds", + * "value":"10" + * }, + * { + * "name":"percentage" + * }, + * { + * "name":"stratified_sampling", + * "value":"true" + * } + * ] + * } + * }, + * { + * "name":"cost_matrix", + * "cost_matrix":[] + * }, + * { + * "name":"evaluation_measures", + * "evaluation_measures": + * { + * "evaluation_measure":"predictive_accuracy" + * } + * } + * ], + * "output":{ + * "name":"predictions", + * "predictions":{ + * "format":"ARFF", + * "feature":[ + * { + * "name":"repeat", + * "type":"integer" + * }, + * { + * "name":"fold", + * "type":"integer" + * }, + * { + * "name":"row_id", + * "type":"integer" + * }, + * { + * "name":"confidence.classname", + * "type":"numeric" + * }, + * { + * "name":"prediction", + * "type":"string" + * } + * ] + * } + * }, + * "tag":["basic","study_1","under100k","under1m"] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n150 - Please provide task_id.\n151 - Unknown task. The task with the given id was not found in the database\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function task($task_id) { $task = $this->Task->getById($task_id); if($task === false) { @@ -196,6 +588,52 @@ private function task_inputs($task_id) { $this->xmlContents('task-inputs', $this->version, array('task' => $task, 'inputs' => $inputs)); } + /** + *@OA\Delete( + * path="/task/{id}", + * tags={"task"}, + * summary="Delete task", + * description="Deletes a task. Upon success, it returns the ID of the deleted task.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_delete", + * ref="#/components/schemas/inline_response_200_4_task_delete", + * ), + * example={ + * "task_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n450 - Please provide API key. In order to remove your content, please authenticate.\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n452 - Task does not exists. The task ID could not be linked to an existing task.\n454 - Task is executed in some runs. Delete these first.\n455 - Deleting the task failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function task_delete($task_id) { $task = $this->Task->getById($task_id); diff --git a/openml_OS/models/api/v1/Api_tasktype.php b/openml_OS/models/api/v1/Api_tasktype.php index f95a9130d..19d382caf 100644 --- a/openml_OS/models/api/v1/Api_tasktype.php +++ b/openml_OS/models/api/v1/Api_tasktype.php @@ -16,11 +16,152 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); + /** + *@OA\Get( + * path="/tasktype/list", + * tags={"tasktype"}, + * summary="List all task types", + * description="Returns an array with all task types in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=default, + * description="Unexpected error", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + * @OA\Response( + * response=200, + * description="A task description", + * @OA\JsonContent( + * ref="#/components/schemas/TaskTypeList", + * example={ + * "task_types":{ + * "task_type":[ + * { + * "id":"1", + * "name":"Supervised Classification", + * "description":"In supervised classification, you are given ...", + * "creator":"Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl" + * }, + * { + * "id":"2", + * "name":"Supervised Regression", + * "description":"Given a dataset with a numeric target ...", + * "creator":"Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl" + * },{} + * ] + * } + * } + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'list') { $this->tasktype_list(); return; } + /** + *@OA\Get( + * path="/tasktype/{id}", + * tags={"tasktype"}, + * summary="Get task type description", + * description="Returns information about a task type. The information includes a description, the given inputs and the expected outputs.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A task type description", + * @OA\JsonContent( + * ref="#/components/schemas/TaskType", + * example={ + * "task_type": { + * "id": "1", + * "name": "Supervised Classification", + * "description": "In supervised classification, you are given an input dataset in which instances are labeled with a certain class. The goal is to build a model that predicts the class for future unlabeled instances. The model is evaluated using a train-test procedure, e.g. cross-validation.

\ + * \ + * To make results by different users comparable, you are given the exact train-test folds to be used, and you need to return at least the predictions generated by your model for each of the test instances. OpenML will use these predictions to calculate a range of evaluation measures on the server.

\ + * \ + * You can also upload your own evaluation measures, provided that the code for doing so is available from the implementation used. For extremely large datasets, it may be infeasible to upload all predictions. In those cases, you need to compute and provide the evaluations yourself.

\ + * \ + * Optionally, you can upload the model trained on all the input data. There is no restriction on the file format, but please use a well-known format or PMML.", + * "creator": [ + * "Joaquin Vanschoren", + * "Jan van Rijn", + * "Luis Torgo", + * "Bernd Bischl" + * ], + * "contributor": [ + * "Bo Gao", + * "Simon Fischer", + * "Venkatesh Umaashankar", + * "Michael Berthold", + * "Bernd Wiswedel", + * "Patrick Winter" + * ], + * "creation_date": "2013-01-24 00:00:00", + * "input": [ + * { + * "name": "source_data", + * "requirement": "required", + * "data_type": "numeric" + * }, + * { + * "name": "target_feature", + * "requirement": "required", + * "data_type": "string" + * }, + * { + * "name": "estimation_procedure", + * "requirement": "required", + * "data_type": "numeric" + * }, + * { + * "name": "cost_matrix", + * "data_type": "json" + * }, + * { + * "name": "custom_testset", + * "data_type": "json" + * }, + * { + * "name": "evaluation_measures", + * "data_type": "string" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n240 - Please provide task type ID.\n241 - Unknown task type. The task type with the given id was not found in the database\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->tasktype($segments[0]); return; diff --git a/openml_OS/models/api/v1/Api_user.php b/openml_OS/models/api/v1/Api_user.php index 14a988483..85595e676 100644 --- a/openml_OS/models/api/v1/Api_user.php +++ b/openml_OS/models/api/v1/Api_user.php @@ -82,6 +82,40 @@ function bootstrap($format, $segments, $request_type, $user_id) { } */ + /** + *@OA\Get( + * path="/user/list", + * tags={"user"}, + * summary="List all users by user id", + * description="Returns an array with all user ids and names.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of users", + * @OA\JsonContent( + * ref="#/components/schemas/UserList", + * example={ + * "users":{ + * "user":[ + * { + * "id":"1", + * "username":"janvanrijn@gmail.com"}, + * { + * "id":"2", + * "username":"joaquin.vanschoren@gmail.com"} + * ] + * } + * } + * ), + * ), + *) + */ private function username_list($segs) { # pass uploader list to get username list $legal_filters = array('user_id'); From d821c29250f5c76fd2f3aef460258bf40ede53bc Mon Sep 17 00:00:00 2001 From: mwever Date: Fri, 7 Feb 2020 17:59:54 +0100 Subject: [PATCH 02/15] Re-arranged some of the api annotations to the bootstrap methods. --- openml_OS/models/api/v1/Api_data.php | 1 + .../models/api/v1/Api_estimationprocedure.php | 108 +-- .../models/api/v1/Api_evaluationmeasure.php | 71 +- openml_OS/models/api/v1/Api_flow.php | 354 ++++---- openml_OS/models/api/v1/Api_run.php | 784 +++++++++--------- openml_OS/models/api/v1/Api_study.php | 224 ++--- openml_OS/models/api/v1/Api_task.php | 325 ++++---- openml_OS/models/api/v1/Api_user.php | 71 +- 8 files changed, 973 insertions(+), 965 deletions(-) diff --git a/openml_OS/models/api/v1/Api_data.php b/openml_OS/models/api/v1/Api_data.php index 493e00b46..d93d3d0ee 100644 --- a/openml_OS/models/api/v1/Api_data.php +++ b/openml_OS/models/api/v1/Api_data.php @@ -1,4 +1,5 @@ outputFormat = $format; - + + /** + *@OA\Get( + * path="/estimationprocedure/list", + * tags={"estimationprocedure"}, + * summary="List all estimation procedures", + * description="Returns an array with all model performance estimation procedures in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of estimation procedures", + * @OA\JsonContent( + * ref="#/components/schemas/EstimationProcedureList", + * example={ + * "estimationprocedures": { + * "estimationprocedure": [ + * { + * "id":"1", + * "ttid":"1", + * "name":"10-fold Crossvalidation", + * "type":"crossvalidation", + * "repeats":"1", + * "folds":"10", + * "stratified_sampling":"true" + * }, + * { + * "id":"2", + * "ttid":"1", + * "name":"5 times 2-fold Crossvalidation", + * "type":"crossvalidation", + * "repeats":"5", + * "folds":"2", + * "stratified_sampling":"true" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n500 - No model performance estimation procedures available.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'list') { $this->estimationprocedure_list(); return; @@ -42,59 +95,6 @@ private function estimationprocedure($id) { $this->xmlContents( 'estimationprocedure-get', $this->version, array( 'ep' => $ep ) ); } - /** - *@OA\Get( - * path="/estimationprocedure/list", - * tags={"estimationprocedure"}, - * summary="List all estimation procedures", - * description="Returns an array with all model performance estimation procedures in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of estimation procedures", - * @OA\JsonContent( - * ref="#/components/schemas/EstimationProcedureList", - * example={ - * "estimationprocedures": { - * "estimationprocedure": [ - * { - * "id":"1", - * "ttid":"1", - * "name":"10-fold Crossvalidation", - * "type":"crossvalidation", - * "repeats":"1", - * "folds":"10", - * "stratified_sampling":"true" - * }, - * { - * "id":"2", - * "ttid":"1", - * "name":"5 times 2-fold Crossvalidation", - * "type":"crossvalidation", - * "repeats":"5", - * "folds":"2", - * "stratified_sampling":"true" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n500 - No model performance estimation procedures available.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function estimationprocedure_list() { $estimationprocedures = $this->Estimation_procedure->get(); diff --git a/openml_OS/models/api/v1/Api_evaluationmeasure.php b/openml_OS/models/api/v1/Api_evaluationmeasure.php index 1aa33afc3..56d01ee05 100644 --- a/openml_OS/models/api/v1/Api_evaluationmeasure.php +++ b/openml_OS/models/api/v1/Api_evaluationmeasure.php @@ -1,4 +1,5 @@ outputFormat = $format; - + + /** + *@OA\Get( + * path="/evaluationmeasure/list", + * tags={"evaluationmeasure"}, + * summary="List all evaluation measures", + * description="Returns an array with all model evaluation measures in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of evaluation measures", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationMeasureList", + * example={ + * "evaluation_measures":{ + * "measures":{ + * "measure":[ + * "area_under_roc_curve", + * "average_cost", + * "binominal_test", + * "build_cpu_time" + * ] + * } + * } + * } + * ), + * ), + *) + */ if (count($segments) == 1 && $segments[0] == 'list') { $this->evaluationmeasure_list(); return; @@ -25,40 +60,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { } - /** - *@OA\Get( - * path="/evaluationmeasure/list", - * tags={"evaluationmeasure"}, - * summary="List all evaluation measures", - * description="Returns an array with all model evaluation measures in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of evaluation measures", - * @OA\JsonContent( - * ref="#/components/schemas/EvaluationMeasureList", - * example={ - * "evaluation_measures":{ - * "measures":{ - * "measure":[ - * "area_under_roc_curve", - * "average_cost", - * "binominal_test", - * "build_cpu_time" - * ] - * } - * } - * } - * ), - * ), - *) - */ private function evaluationmeasure_list() { $data = new stdClass(); $data->measures = $this->Math_function->getWhere( 'functionType = "EvaluationFunction"' ); diff --git a/openml_OS/models/api/v1/Api_flow.php b/openml_OS/models/api/v1/Api_flow.php index 9616bece4..7086b2d8b 100644 --- a/openml_OS/models/api/v1/Api_flow.php +++ b/openml_OS/models/api/v1/Api_flow.php @@ -1,4 +1,5 @@ = 1 && $segments[0] == 'list') { array_shift($segments); $this->flow_list($segments); @@ -34,6 +106,59 @@ function bootstrap($format, $segments, $request_type, $user_id) { } // TODO: deprecate! + /** + *@OA\Get( + * path="/flow/exists/{name}/{version}", + * tags={"flow"}, + * summary="Check whether flow exists", + * description="Checks whether a flow with the given name and (external) version exists.", + * @OA\Parameter( + * name="name", + * in="path", + * type="string", + * description="The name of the flow.", + * required="true", + * ), + * @OA\Parameter( + * name="version", + * in="path", + * type="string", + * description="The external version of the flow", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of flows", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_exists", + * ref="#/components/schemas/inline_response_200_10_flow_exists", + * ), + * example={ + * "flow_exists": { + * "exists": "true", + * "id": "65" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n330 - Mandatory fields not present. Please provide name and external_version.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 3 && $segments[0] == 'exists') { $this->flow_exists($segments[1],$segments[2]); return; @@ -169,6 +294,58 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Post( + * path="/flow", + * tags={"flow"}, + * summary="Upload a flow", + * description="Uploads a flow. Upon success, it returns the flow id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", + * required="true", + * ), + * @OA\Parameter( + * name="flow", + * in="formData", + * type="file", + * description="The actual flow, being a source (or binary) file.", + * required="false", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_9_upload_flow", + * ), + * example={ + * "upload_flow": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n160 - Error in file uploading. There was a problem with the file upload.\n161 - Please provide description xml.\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\n164 - Flow already stored in database. Please change name or version number\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\n166 - Failed to add flow to database. Internal server error, please contact API administrators\n167 - Illegal files uploaded. An non required file was uploaded.\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n171 - Flow already exists. This flow is already in the database\n172 - XSD not found. Please contact API support team\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 0 && $request_type == 'post') { $this->flow_upload(); return; @@ -297,77 +474,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } - /** - *@OA\Get( - * path="/flow/list/{filters}", - * tags={"flow"}, - * summary="List and filter flows", - * description="List flows, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all flows that match the constraints.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. - /tag/{tag} - returns only tasks tagged with the given tag. - /uploader/{id} - return only evaluations uploaded by a specific user, specified by user ID. - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of flows", - * @OA\JsonContent( - * ref="#/components/schemas/FlowList", - * example={ - * "flows": - * { - * "flow":[ - * { - * "id":"65", - * "full_name":"weka.RandomForest(1)", - * "name":"weka.RandomForest", - * "version":"1", - * "external_version":"Weka_3.7.10_9186", - * "uploader":"1" - * }, - * { - * "id":"66", - * "full_name":"weka.IBk(1)", - * "name":"weka.IBk", - * "version":"1", - * "external_version":"Weka_3.7.10_8034", - * "uploader":"1" - * }, - * { - * "id":"67", - * "full_name":"weka.BayesNet_K2(1)", - * "name":"weka.BayesNet_K2", - * "version":"1", - * "external_version":"Weka_3.7.10_8034", - * "uploader":"1" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n500 - No results. There where no matches for the given constraints.\n501 - Illegal filter specified.\n502 - Filter values/ranges not properly specified.\n503 - Can not specify an offset without a limit.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function flow_list($segs) { $legal_filters = array('uploader', 'tag', 'limit', 'offset'); @@ -422,59 +528,6 @@ private function flow_list($segs) { } - /** - *@OA\Get( - * path="/flow/exists/{name}/{version}", - * tags={"flow"}, - * summary="Check whether flow exists", - * description="Checks whether a flow with the given name and (external) version exists.", - * @OA\Parameter( - * name="name", - * in="path", - * type="string", - * description="The name of the flow.", - * required="true", - * ), - * @OA\Parameter( - * name="version", - * in="path", - * type="string", - * description="The external version of the flow", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of flows", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_exists", - * ref="#/components/schemas/inline_response_200_10_flow_exists", - * ), - * example={ - * "flow_exists": { - * "exists": "true", - * "id": "65" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n330 - Mandatory fields not present. Please provide name and external_version.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function flow_exists($name, $external_version) { $similar = false; @@ -491,58 +544,7 @@ private function flow_exists($name, $external_version) { } $this->xmlContents( 'implementation-exists', $this->version, $result ); } - /** - *@OA\Post( - * path="/flow", - * tags={"flow"}, - * summary="Upload a flow", - * description="Uploads a flow. Upon success, it returns the flow id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", - * required="true", - * ), - * @OA\Parameter( - * name="flow", - * in="formData", - * type="file", - * description="The actual flow, being a source (or binary) file.", - * required="false", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded flow", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_9_upload_flow", - * ), - * example={ - * "upload_flow": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n160 - Error in file uploading. There was a problem with the file upload.\n161 - Please provide description xml.\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\n164 - Flow already stored in database. Please change name or version number\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\n166 - Failed to add flow to database. Internal server error, please contact API administrators\n167 - Illegal files uploaded. An non required file was uploaded.\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n171 - Flow already exists. This flow is already in the database\n172 - XSD not found. Please contact API support team\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ + // TODO: check what is going wrong with implementation id 1 private function flow($id) { if( $id == false ) { diff --git a/openml_OS/models/api/v1/Api_run.php b/openml_OS/models/api/v1/Api_run.php index 9940b0104..2a5cb1516 100644 --- a/openml_OS/models/api/v1/Api_run.php +++ b/openml_OS/models/api/v1/Api_run.php @@ -1,4 +1,6 @@ run_trace_upload(); return; } + /** + *@OA\Get( + * path="/run/trace/{id}", + * tags={"run"}, + * summary="Get run trace", + * description="Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A run trace description", + * @OA\JsonContent( + * ref="#/components/schemas/RunTrace", + * example={ + * "trace": { + * "run_id":"573055", + * "trace_iteration": { + * "repeat":"0", + * "fold":"0", + * "repeat":"0", + * "iteration":"0", + * "setup_string":{"parameter_minNumObj": "1", + * "parameter_confidenceFactor": "0.1"}, + * "evaluation":"94.814815", + * "selected": "true" + * }, + * "trace_iteration": { + * "repeat":"0", + * "fold":"0", + * "repeat":"0", + * "iteration":"1", + * "setup_string":{"parameter_minNumObj": "1", + * "parameter_confidenceFactor": "0.25"}, + * "evaluation": "94.074074", + * "selected": "true" + * } + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n570 - No successful trace associated with this run\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && $segments[0] == 'trace' && is_numeric($segments[1])) { $this->run_trace($segments[1]); return; } + /** + *@OA\Get( + * path="/run/{id}", + * tags={"run"}, + * summary="Get run description", + * description="Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A run description", + * @OA\JsonContent( + * ref="#/components/schemas/Run", + * example={ + * "run": { + * "run_id":"100", + * "uploader":"1", + * "uploader_name":"Jan van Rijn", + * "task_id":"28", + * "task_type":"Supervised Classification", + * "task_evaluation_measure":"predictive_accuracy", + * "flow_id":"67", + * "flow_name":"weka.BayesNet_K2(1)", + * "setup_string":"weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", + * "parameter_setting": [ + * { + * "name":"D", + * "value":"true" + * }, + * { + * "name":"Q", + * "value":"weka.classifiers.bayes.net.search.local.K2" + * }, + * { + * "name":"P", + * "value":"1" + * }, + * { + * "name":"S", + * "value":"BAYES" + * } + * ], + * "input_data": + * { + * "dataset": + * { + * "did":"28", + * "name":"optdigits", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/28\\/dataset_28_optdigits.arff" + * } + * }, + * "output_data": + * { + * "file": [ + * { + * "did":"48838", + * "file_id":"261", + * "name":"description", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/261\\/weka_generated_run935374685998857626.xml" + * }, + * { + * "did":"48839", + * "file_id":"262", + * "name":"predictions", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff" + * } + * ], + * "evaluation": [ + * { + * "name":"area_under_roc_curve", + * "flow_id":"4", + * "value":"0.990288", + * "array_data":"[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]" + * }, + * { + * "name":"confusion_matrix", + * "flow_id":"10", + * "array_data":"[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]" + * }, + * { + * "name":"f_measure", + * "flow_id":"12", + * "value":"0.922723", + * "array_data":"[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]" + * }, + * { + * "name":"kappa", + * "flow_id":"13", + * "value":"0.913601" + * } + * ] + * } + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n220 - Please provide run ID. In order to view run details, please provide the run ID.\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->run($segments[0]); return; } + /** + *@OA\Get( + * path="/run/reset/{id}", + * tags={"run"}, + * summary="Resets a run.", + * description="Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.", + * @OA\Parameter( + * name="id", + * in="path", + * type="string", + * description="Run ID.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="Id of the evaluated run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_reset", + * ref="#/components/schemas/inline_response_200_21_upload_flow", + * ), + * example={ + * "run_reset": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n412 - Run does not exist\n413 - Run is not owned by you\n394 - Resetting run failed\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && is_numeric($segments[1]) && $segments[0] == 'reset' && in_array($request_type, $getpost)) { $this->run_reset($segments[1]); return; } + /** + *@OA\Delete( + * path="/run/{id}", + * tags={"run"}, + * summary="Delete run", + * description="Deletes a run. Upon success, it returns the ID of the deleted run.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the run.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_delete", + * ref="#/components/schemas/inline_response_200_17_data_delete", + * ), + * example={ + * "run_delete": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n390 - Please provide API key. In order to remove your content, please authenticate.\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\n392 - Run does not exists. The run ID could not be linked to an existing run.\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->run_delete($segments[0]); return; } + /** + *@OA\Post( + * path="/run", + * tags={"run"}, + * summary="Upload run", + * description="Uploads a run. Upon success, it returns the run id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", + * required="true", + * ), + * @OA\Parameter( + * name="predictions", + * in="formData", + * type="file", + * description="The predictions generated by the run", + * required="true", + * ), + * @OA\Parameter( + * name="model_readable", + * in="formData", + * type="file", + * description="The human-readable model generated by the run", + * required="false", + * ), + * @OA\Parameter( + * name="model_serialized", + * in="formData", + * type="file", + * description="The serialized model generated by the run", + * required="false", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_18_upload_flow", + * ), + * example={ + * "upload_run": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n202 - Please provide run XML.\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\n204 - Unknown task. The task with the given ID was not found in the database.\n205 - Unknown flow. The flow with the given ID was not found in the database.\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\n207 - File upload failed. One of the files uploaded has a problem.\n208 - Error inserting setup record. Please contact api administrators\n210 - Unable to store run. Please contact api administrators.\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\n212 - Unable to store file. Please contact api administrators.\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\n214 - Unable to store input setting. Please contact API support team.\n215 - Unable to evaluate predictions. Please contact API support team.\n216 - Error thrown by Java Application. Additional information field is provided.\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\n219 - Error reading the XML document. The XML description file could not be verified.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 0 && $request_type == 'post') { $this->run_upload(); return; @@ -397,124 +789,6 @@ private function run_list($segs, $user_id) { $this->xmlContents('runs', $this->version, array('runs' => $res)); } - /** - *@OA\Get( - * path="/run/{id}", - * tags={"run"}, - * summary="Get run description", - * description="Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A run description", - * @OA\JsonContent( - * ref="#/components/schemas/Run", - * example={ - * "run": { - * "run_id":"100", - * "uploader":"1", - * "uploader_name":"Jan van Rijn", - * "task_id":"28", - * "task_type":"Supervised Classification", - * "task_evaluation_measure":"predictive_accuracy", - * "flow_id":"67", - * "flow_name":"weka.BayesNet_K2(1)", - * "setup_string":"weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", - * "parameter_setting": [ - * { - * "name":"D", - * "value":"true" - * }, - * { - * "name":"Q", - * "value":"weka.classifiers.bayes.net.search.local.K2" - * }, - * { - * "name":"P", - * "value":"1" - * }, - * { - * "name":"S", - * "value":"BAYES" - * } - * ], - * "input_data": - * { - * "dataset": - * { - * "did":"28", - * "name":"optdigits", - * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/28\\/dataset_28_optdigits.arff" - * } - * }, - * "output_data": - * { - * "file": [ - * { - * "did":"48838", - * "file_id":"261", - * "name":"description", - * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/261\\/weka_generated_run935374685998857626.xml" - * }, - * { - * "did":"48839", - * "file_id":"262", - * "name":"predictions", - * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff" - * } - * ], - * "evaluation": [ - * { - * "name":"area_under_roc_curve", - * "flow_id":"4", - * "value":"0.990288", - * "array_data":"[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]" - * }, - * { - * "name":"confusion_matrix", - * "flow_id":"10", - * "array_data":"[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]" - * }, - * { - * "name":"f_measure", - * "flow_id":"12", - * "value":"0.922723", - * "array_data":"[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]" - * }, - * { - * "name":"kappa", - * "flow_id":"13", - * "value":"0.913601" - * } - * ] - * } - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n220 - Please provide run ID. In order to view run details, please provide the run ID.\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function run($run_id) { if( $run_id == false ) { $this->returnError( 235, $this->version ); @@ -540,52 +814,7 @@ private function run($run_id) { $this->xmlContents( 'run-get', $this->version, array( 'source' => $run ) ); } - /** - *@OA\Delete( - * path="/run/{id}", - * tags={"run"}, - * summary="Delete run", - * description="Deletes a run. Upon success, it returns the ID of the deleted run.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="data_delete", - * ref="#/components/schemas/inline_response_200_17_data_delete", - * ), - * example={ - * "run_delete": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n390 - Please provide API key. In order to remove your content, please authenticate.\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\n392 - Run does not exists. The run ID could not be linked to an existing run.\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ + private function run_delete($run_id) { $run = $this->Run->getById( $run_id ); @@ -635,52 +864,6 @@ private function run_delete($run_id) { $this->xmlContents( 'run-delete', $this->version, array( 'run' => $run ) ); } - - /** - *@OA\Get( - * path="/run/reset/{id}", - * tags={"run"}, - * summary="Resets a run.", - * description="Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.", - * @OA\Parameter( - * name="id", - * in="path", - * type="string", - * description="Run ID.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="Id of the evaluated run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="run_reset", - * ref="#/components/schemas/inline_response_200_21_upload_flow", - * ), - * example={ - * "run_reset": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n412 - Run does not exist\n413 - Run is not owned by you\n394 - Resetting run failed\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function run_reset($run_id) { $run = $this->Run->getById( $run_id ); @@ -703,72 +886,6 @@ private function run_reset($run_id) { $this->xmlContents( 'run-reset', $this->version, array( 'run' => $run ) ); } - /** - *@OA\Post( - * path="/run", - * tags={"run"}, - * summary="Upload run", - * description="Uploads a run. Upon success, it returns the run id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", - * required="true", - * ), - * @OA\Parameter( - * name="predictions", - * in="formData", - * type="file", - * description="The predictions generated by the run", - * required="true", - * ), - * @OA\Parameter( - * name="model_readable", - * in="formData", - * type="file", - * description="The human-readable model generated by the run", - * required="false", - * ), - * @OA\Parameter( - * name="model_serialized", - * in="formData", - * type="file", - * description="The serialized model generated by the run", - * required="false", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_18_upload_flow", - * ), - * example={ - * "upload_run": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n202 - Please provide run XML.\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\n204 - Unknown task. The task with the given ID was not found in the database.\n205 - Unknown flow. The flow with the given ID was not found in the database.\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\n207 - File upload failed. One of the files uploaded has a problem.\n208 - Error inserting setup record. Please contact api administrators\n210 - Unable to store run. Please contact api administrators.\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\n212 - Unable to store file. Please contact api administrators.\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\n214 - Unable to store input setting. Please contact API support team.\n215 - Unable to evaluate predictions. Please contact API support team.\n216 - Error thrown by Java Application. Additional information field is provided.\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\n219 - Error reading the XML document. The XML description file could not be verified.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function run_upload() { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -1071,68 +1188,6 @@ private function run_upload() { $this->xmlContents( 'run-upload', $this->version, $result ); } - /** - *@OA\Get( - * path="/run/trace/{id}", - * tags={"run"}, - * summary="Get run trace", - * description="Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A run trace description", - * @OA\JsonContent( - * ref="#/components/schemas/RunTrace", - * example={ - * "trace": { - * "run_id":"573055", - * "trace_iteration": { - * "repeat":"0", - * "fold":"0", - * "repeat":"0", - * "iteration":"0", - * "setup_string":{"parameter_minNumObj": "1", - * "parameter_confidenceFactor": "0.1"}, - * "evaluation":"94.814815", - * "selected": "true" - * }, - * "trace_iteration": { - * "repeat":"0", - * "fold":"0", - * "repeat":"0", - * "iteration":"1", - * "setup_string":{"parameter_minNumObj": "1", - * "parameter_confidenceFactor": "0.25"}, - * "evaluation": "94.074074", - * "selected": "true" - * } - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n570 - No successful trace associated with this run\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function run_trace($run_id) { $run = $this->Run->getById($run_id); if ($run === false) { @@ -1149,59 +1204,6 @@ private function run_trace($run_id) { $this->xmlContents('run-trace-get', $this->version, array('run_id' => $run_id, 'trace' => $trace)); } - /** - *@OA\Post( - * path="/run/trace/{id}", - * tags={"run"}, - * summary="Upload run trace", - * description="Uploads a run trace. Upon success, it returns the run id.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the run with the trace", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_23_upload_flow", - * ), - * example={ - * "run_trace": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n561 - Problem with uploaded trace file.\n562 - Problem validating xml trace file.\n563 - Problem loading xml trace file.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function run_trace_upload() { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); diff --git a/openml_OS/models/api/v1/Api_study.php b/openml_OS/models/api/v1/Api_study.php index b623abf3d..3ef951359 100644 --- a/openml_OS/models/api/v1/Api_study.php +++ b/openml_OS/models/api/v1/Api_study.php @@ -187,12 +187,122 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->study_delete($segments[0]); return; } - + + /** + *@OA\Post( + * path="/study/{id}/attach", + * tags={"study"}, + * summary="Attach a new entity to a study", + * description="Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study. Supplied in the URL path.", + * required="true", + * ), + * @OA\Parameter( + * name="ids", + * in="formData", + * type="string", + * description="Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Properties of the updated study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_attach", + * ref="#/components/schemas/inline_response_200_26_study_attach", + * ), + * example={ + * "study_attach": { + * "id": "1", + * "main_entity_type": "task", + * "linked_entities": "5" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannnot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && is_numeric($segments[0]) && $segments[1] == 'attach' && $request_type == 'post') { $this->study_attach_detach($segments[0], true); return; } - + + /** + *@OA\Post( + * path="/study/{id}/detach", + * tags={"study"}, + * summary="Detach an entity from a study", + * description="Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study.", + * required="true", + * ), + * @OA\Parameter( + * name="ids", + * in="formData", + * type="string", + * description="Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="Properties of the updated study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_study", + * ref="#/components/schemas/inline_response_200_26_study_attach", + * ), + * example={ + * "study_detach": { + * "id": "1", + * "main_entity_type": "task", + * "linked_entities": "5" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 2 && is_numeric($segments[0]) && $segments[1] == 'detach' && $request_type == 'post') { $this->study_attach_detach($segments[0], false); return; @@ -406,116 +516,6 @@ private function status_update($study_id, $status) { $this->xmlContents('study-status-update', $this->version, $template_vars); } - /** - *@OA\Post( - * path="/study/{id}/attach", - * tags={"study"}, - * summary="Attach a new entity to a study", - * description="Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the study. Supplied in the URL path.", - * required="true", - * ), - * @OA\Parameter( - * name="ids", - * in="formData", - * type="string", - * description="Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Properties of the updated study", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="study_attach", - * ref="#/components/schemas/inline_response_200_26_study_attach", - * ), - * example={ - * "study_attach": { - * "id": "1", - * "main_entity_type": "task", - * "linked_entities": "5" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannnot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ - /** - *@OA\Post( - * path="/study/{id}/detach", - * tags={"study"}, - * summary="Detach an entity from a study", - * description="Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the study.", - * required="true", - * ), - * @OA\Parameter( - * name="ids", - * in="formData", - * type="string", - * description="Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Properties of the updated study", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_study", - * ref="#/components/schemas/inline_response_200_26_study_attach", - * ), - * example={ - * "study_detach": { - * "id": "1", - * "main_entity_type": "task", - * "linked_entities": "5" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function study_attach_detach($study_id, $attach) { $study = $this->Study->getById($study_id); if ($study === false) { diff --git a/openml_OS/models/api/v1/Api_task.php b/openml_OS/models/api/v1/Api_task.php index 725cf0556..85bb4d62c 100644 --- a/openml_OS/models/api/v1/Api_task.php +++ b/openml_OS/models/api/v1/Api_task.php @@ -26,6 +26,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->outputFormat = $format; $getpost = array('get','post'); + /** *@OA\Get( * path="/task/list/{filters}", @@ -160,6 +161,122 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Get( + * path="/task/{id}", + * tags={"task"}, + * summary="Get task description", + * description="Returns information about a task. The information includes the task type, input data, train/test sets, and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A task description", + * @OA\JsonContent( + * ref="#/components/schemas/Task", + * example={ + * "task": { + * "task_id":"1", + * "task_type":"Supervised Classification", + * "input":[ + * { + * "name":"source_data", + * "data_set":{ + * "data_set_id":"1", + * "target_feature":"class" + * } + * }, + * { + * "name":"estimation_procedure", + * "estimation_procedure":{ + * "type":"crossvalidation", + * "data_splits_url":"https://www.openml.org/api_splits/get/1/Task_1_splits.arff", + * "parameter":[ + * { + * "name":"number_repeats", + * "value":"1" + * }, + * { + * "name":"number_folds", + * "value":"10" + * }, + * { + * "name":"percentage" + * }, + * { + * "name":"stratified_sampling", + * "value":"true" + * } + * ] + * } + * }, + * { + * "name":"cost_matrix", + * "cost_matrix":[] + * }, + * { + * "name":"evaluation_measures", + * "evaluation_measures": + * { + * "evaluation_measure":"predictive_accuracy" + * } + * } + * ], + * "output":{ + * "name":"predictions", + * "predictions":{ + * "format":"ARFF", + * "feature":[ + * { + * "name":"repeat", + * "type":"integer" + * }, + * { + * "name":"fold", + * "type":"integer" + * }, + * { + * "name":"row_id", + * "type":"integer" + * }, + * { + * "name":"confidence.classname", + * "type":"numeric" + * }, + * { + * "name":"prediction", + * "type":"string" + * } + * ] + * } + * }, + * "tag":["basic","study_1","under100k","under1m"] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n150 - Please provide task_id.\n151 - Unknown task. The task with the given id was not found in the database\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->task($segments[0]); return; @@ -220,6 +337,52 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } + /** + *@OA\Delete( + * path="/task/{id}", + * tags={"task"}, + * summary="Delete task", + * description="Deletes a task. Upon success, it returns the ID of the deleted task.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the task.", + * required="true", + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required="true", + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_delete", + * ref="#/components/schemas/inline_response_200_4_task_delete", + * ), + * example={ + * "task_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n450 - Please provide API key. In order to remove your content, please authenticate.\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n452 - Task does not exists. The task ID could not be linked to an existing task.\n454 - Task is executed in some runs. Delete these first.\n455 - Deleting the task failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->task_delete($segments[0]); return; @@ -426,122 +589,6 @@ private function task_list($segs, $user_id) { $this->xmlContents( 'tasks', $this->version, array( 'tasks' => $tasks_res ) ); } - /** - *@OA\Get( - * path="/task/{id}", - * tags={"task"}, - * summary="Get task description", - * description="Returns information about a task. The information includes the task type, input data, train/test sets, and more.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A task description", - * @OA\JsonContent( - * ref="#/components/schemas/Task", - * example={ - * "task": { - * "task_id":"1", - * "task_type":"Supervised Classification", - * "input":[ - * { - * "name":"source_data", - * "data_set":{ - * "data_set_id":"1", - * "target_feature":"class" - * } - * }, - * { - * "name":"estimation_procedure", - * "estimation_procedure":{ - * "type":"crossvalidation", - * "data_splits_url":"https://www.openml.org/api_splits/get/1/Task_1_splits.arff", - * "parameter":[ - * { - * "name":"number_repeats", - * "value":"1" - * }, - * { - * "name":"number_folds", - * "value":"10" - * }, - * { - * "name":"percentage" - * }, - * { - * "name":"stratified_sampling", - * "value":"true" - * } - * ] - * } - * }, - * { - * "name":"cost_matrix", - * "cost_matrix":[] - * }, - * { - * "name":"evaluation_measures", - * "evaluation_measures": - * { - * "evaluation_measure":"predictive_accuracy" - * } - * } - * ], - * "output":{ - * "name":"predictions", - * "predictions":{ - * "format":"ARFF", - * "feature":[ - * { - * "name":"repeat", - * "type":"integer" - * }, - * { - * "name":"fold", - * "type":"integer" - * }, - * { - * "name":"row_id", - * "type":"integer" - * }, - * { - * "name":"confidence.classname", - * "type":"numeric" - * }, - * { - * "name":"prediction", - * "type":"string" - * } - * ] - * } - * }, - * "tag":["basic","study_1","under100k","under1m"] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n150 - Please provide task_id.\n151 - Unknown task. The task with the given id was not found in the database\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function task($task_id) { $task = $this->Task->getById($task_id); if($task === false) { @@ -588,52 +635,6 @@ private function task_inputs($task_id) { $this->xmlContents('task-inputs', $this->version, array('task' => $task, 'inputs' => $inputs)); } - /** - *@OA\Delete( - * path="/task/{id}", - * tags={"task"}, - * summary="Delete task", - * description="Deletes a task. Upon success, it returns the ID of the deleted task.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted task", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="task_delete", - * ref="#/components/schemas/inline_response_200_4_task_delete", - * ), - * example={ - * "task_delete": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n450 - Please provide API key. In order to remove your content, please authenticate.\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n452 - Task does not exists. The task ID could not be linked to an existing task.\n454 - Task is executed in some runs. Delete these first.\n455 - Deleting the task failed. Please contact support team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ private function task_delete($task_id) { $task = $this->Task->getById($task_id); diff --git a/openml_OS/models/api/v1/Api_user.php b/openml_OS/models/api/v1/Api_user.php index 85595e676..29446978b 100644 --- a/openml_OS/models/api/v1/Api_user.php +++ b/openml_OS/models/api/v1/Api_user.php @@ -17,12 +17,47 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->outputFormat = $format; # http://test.openml.org/api/v1/user/list/uploader/1,2 + + /** + *@OA\Get( + * path="/user/list", + * tags={"user"}, + * summary="List all users by user id", + * description="Returns an array with all user ids and names.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required="false", + * ), + * @OA\Response( + * response=200, + * description="A list of users", + * @OA\JsonContent( + * ref="#/components/schemas/UserList", + * example={ + * "users":{ + * "user":[ + * { + * "id":"1", + * "username":"janvanrijn@gmail.com"}, + * { + * "id":"2", + * "username":"joaquin.vanschoren@gmail.com"} + * ] + * } + * } + * ), + * ), + *) + */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->username_list($segments); return; } - + /*$getpost = array('get','post'); if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { @@ -82,40 +117,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { } */ - /** - *@OA\Get( - * path="/user/list", - * tags={"user"}, - * summary="List all users by user id", - * description="Returns an array with all user ids and names.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of users", - * @OA\JsonContent( - * ref="#/components/schemas/UserList", - * example={ - * "users":{ - * "user":[ - * { - * "id":"1", - * "username":"janvanrijn@gmail.com"}, - * { - * "id":"2", - * "username":"joaquin.vanschoren@gmail.com"} - * ] - * } - * } - * ), - * ), - *) - */ private function username_list($segs) { # pass uploader list to get username list $legal_filters = array('user_id'); From 2b8f5ba34bce045e4bd63e2e31a02142044d5ba1 Mon Sep 17 00:00:00 2001 From: mwever Date: Fri, 7 Feb 2020 18:06:10 +0100 Subject: [PATCH 03/15] Added swagger-php docs for schemas used as responses in the API. --- openml_OS/controllers/Api.php | 2095 ++++++++++++++++++++++++++++++++- 1 file changed, 2090 insertions(+), 5 deletions(-) diff --git a/openml_OS/controllers/Api.php b/openml_OS/controllers/Api.php index 43eb01b8b..c17f2a0fa 100644 --- a/openml_OS/controllers/Api.php +++ b/openml_OS/controllers/Api.php @@ -1,12 +1,2097 @@ From 178aa2ddfb0d73dc810ab1282702386080c9910f Mon Sep 17 00:00:00 2001 From: mwever Date: Wed, 8 Jul 2020 11:56:59 +0200 Subject: [PATCH 04/15] Moved annotations from the bootstrap to the respective methods responsible for handling the logic. Refactored bootstrapping for tag/untag of several entities as well as attach/detach to clearly distinct between different handlers. Fixed boolean types in annotations. --- openml_OS/models/api/v1/Api_data.php | 1539 +++++++++-------- .../models/api/v1/Api_estimationprocedure.php | 107 +- openml_OS/models/api/v1/Api_evaluation.php | 187 +- .../models/api/v1/Api_evaluationmeasure.php | 69 +- openml_OS/models/api/v1/Api_flow.php | 799 ++++----- openml_OS/models/api/v1/Api_run.php | 1106 ++++++------ openml_OS/models/api/v1/Api_setup.php | 602 +++---- openml_OS/models/api/v1/Api_study.php | 660 +++---- openml_OS/models/api/v1/Api_task.php | 895 +++++----- openml_OS/models/api/v1/Api_user.php | 68 +- 10 files changed, 3050 insertions(+), 2982 deletions(-) diff --git a/openml_OS/models/api/v1/Api_data.php b/openml_OS/models/api/v1/Api_data.php index cea55ef8d..2d662d40d 100644 --- a/openml_OS/models/api/v1/Api_data.php +++ b/openml_OS/models/api/v1/Api_data.php @@ -31,173 +31,12 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->outputFormat = $format; $getpost = array('get','post'); - - - /** - *@OA\Get( - * path="/data/list/{filters}", - * tags={"data"}, - * summary="List and filter datasets", - * description="List datasets, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/data/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all datasets that match the constraints.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. - /status/{status} - returns only datasets with a given status, either 'active', 'deactivated', or 'in_preparation'. - /tag/{tag} - returns only datasets tagged with the given tag. - /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, data_version, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'. - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of datasets with the given task", - * @OA\JsonContent( - * ref="#/components/schemas/DataList", - * example={ - * "data": { - * "dataset": [ - * { - * "did":"1", - * "name":"anneal", - * "status":"active", - * "format":"ARFF", - * "quality":[ - * { - * "name":"MajorityClassSize", - * "value":"684" - * }, - * { - * "name":"MaxNominalAttDistinctValues", - * "value":"10.0" - * }, - * { - * "name":"MinorityClassSize" - * ,"value":"0" - * }, - * { - * "name":"NumBinaryAtts", - * "value":"14.0" - * }, - * { - * "name":"NumberOfClasses", - * "value":"6" - * }, - * { - * "name":"NumberOfFeatures", - * "value":"39" - * }, - * { - * "name":"NumberOfInstances", - * "value":"898" - * }, - * { - * "name":"NumberOfInstancesWithMissingValues", - * "value":"0" - * }, - * { - * "name":"NumberOfMissingValues", - * "value":"0" - * }, - * { - * "name":"NumberOfNumericFeatures", - * "value":"6" - * }, - * { - * "name":"NumberOfSymbolicFeatures", - * "value":"32" - * } - * ] - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n370 - Illegal filter specified.\n371 - Filter values/ranges not properly specified.\n372 - No results. There where no matches for the given constraints.\n373 - Can not specify an offset without a limit.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->data_list($segments); return; } - /** - *@OA\Get( - * path="/data/{id}", - * tags={"data"}, - * summary="Get dataset description", - * description="Returns information about a dataset. The information includes the name, information about the creator, URL to download it and more.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A dataset description", - * @OA\JsonContent( - * ref="#/components/schemas/Data", - * example={ - * "data_set_description": { - * "id": "1", - * "name": "anneal", - * "version": "2", - * "description": "...", - * "format": "ARFF", - * "upload_date": "2014-04-06 23:19:20", - * "licence": "Public", - * "url": "https://www.openml.org/data/download/1/dataset_1_anneal.arff", - * "file_id": "1", - * "default_target_attribute": "class", - * "version_label": "2", - * "tag": [ - * "study_1", - * "uci" - * ], - * "visibility": "public", - * "original_data_url": "https://www.openml.org/d/2", - * "status": "active", - * "md5_checksum": "d01f6ccd68c88b749b20bbe897de3713" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned\n110 - Please provide data_id.\n111 - Unknown dataset. Data set description with data_id was not found in the database.\n112 - No access granted. This dataset is not shared with you.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->data($segments[0]); return; @@ -205,106 +44,11 @@ function bootstrap($format, $segments, $request_type, $user_id) { $order_values = array('random', 'normal'); - /** - *@OA\Get( - * path="/data/unprocessed/{data_engine_id}/{order}", - * tags={"data"}, - * summary="Get a list of unprocessed datasets", - * description="This call is for people running their own dataset processing engines. It returns the details of datasets that are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", - * @OA\Parameter( - * name="data_engine_id", - * in="path", - * type="string", - * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", - * required="true", - * ), - * @OA\Parameter( - * name="order", - * in="path", - * type="string", - * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of unprocessed datasets", - * @OA\JsonContent( - * ref="#/components/schemas/DataUnprocessed", - * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n681 - No unprocessed datasets.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 3 && $segments[0] == 'unprocessed' && is_numeric($segments[1]) && in_array($segments[2], $order_values)) { $this->data_unprocessed($segments[1], $segments[2]); return; } - /** - *@OA\Post( - * path="/data/qualities/unprocessed/{data_engine_id}/{order}", - * tags={"data"}, - * summary="Get a list of datasets with unprocessed qualities", - * description="This call is for people running their own dataset processing engines. It returns the details of datasets for which certain qualities are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", - * @OA\Parameter( - * name="data_engine_id", - * in="path", - * type="string", - * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", - * required="true", - * ), - * @OA\Parameter( - * name="order", - * in="path", - * type="string", - * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Parameter( - * name="qualities", - * in="formData", - * type="string", - * description="Comma-separated list of (at least two) quality names, e.g. 'NumberOfInstances,NumberOfFeatures'.", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="A list of unprocessed datasets", - * @OA\JsonContent( - * ref="#/components/schemas/DataUnprocessed", - * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n686 - Please specify the features the evaluation engine wants to calculate (at least 2).\n687 - No unprocessed datasets according to the given set of meta-features.\n688 - Illegal qualities.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) >= 4 && count($segments) <= 6 && $segments[0] == 'qualities' && $segments[1] == 'unprocessed' && is_numeric($segments[2]) && in_array($segments[3], $order_values)) { $feature = (count($segments) > 4 && $segments[4] == 'feature'); // oops, badly defined api call with two optional parameters. boolean feature and string priority tag. @@ -321,52 +65,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Delete( - * path="/data/{id}", - * tags={"data"}, - * summary="Delete dataset", - * description="Deletes a dataset. Upon success, it returns the ID of the deleted dataset.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted dataset", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="data_delete", - * ref="#/components/schemas/inline_response_200_data_delete", - * ), - * example={ - * "data_delete": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned\n- 350 - Please provide API key. In order to remove your content, please authenticate.\n- 351 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n- 352 - Dataset does not exists. The data ID could not be linked to an existing dataset.\n- 353 - Dataset is not owned by you. The dataset is owned by another user. Hence you cannot delete it.\n- 354 - Dataset is in use by other content. Can not be deleted. The data is used in tasks or runs. Delete other content before deleting this dataset.\n- 355 - Deleting dataset failed. Deleting the dataset failed. Please contact support team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->data_delete($segments[0]); return; @@ -377,311 +75,26 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Post( - * path="/data", - * tags={"data"}, - * summary="Upload dataset", - * description="Uploads a dataset. Upon success, it returns the data id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/data).", - * required="true", - * ), - * @OA\Parameter( - * name="dataset", - * in="formData", - * type="file", - * description="The actual dataset, being an ARFF file.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded dataset", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_data_set", - * ref="#/components/schemas/inline_response_200_1_upload_data_set", - * ), - * example={ - * "upload_data_set": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n130 - Problem with file uploading. There was a problem with the file upload.\n131 - Problem validating uploaded description file. The XML description format does not meet the standards.\n132 - Failed to move the files. Internal server error, please contact API administrators.\n133 - Failed to make checksum of datafile. Internal server error, please contact API administrators.\n134 - Failed to insert record in database. Internal server error, please contact API administrators.\n135 - Please provide description xml.\n136 - File failed format verification. The uploaded file is not valid according to the selected file format. Please check the file format specification and try again.\n137 - Please provide API key. In order to share content, please log in or provide your API key.\n138 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n139 - Combination name / version already exists. Leave version out for auto increment\n140 - Both dataset file and dataset url provided. The system is confused since both a dataset file (post) and a dataset url (xml) are provided. Please remove one.\n141 - Neither dataset file or dataset url are provided. Please provide either a dataset file as POST variable, or a dataset url in the description XML.\n142 - Error in processing arff file. Can be a syntax error, or the specified target feature does not exists. For now, we only check on arff files. If a dataset is claimed to be in such a format, and it can not be parsed, this error is returned.\n143 - Suggested target feature not legal. It is possible to suggest a default target feature (for predictive tasks). However, it should be provided in the data.\n144 - Unable to update dataset. The dataset with id could not be found in the database. If you upload a new dataset, unset the id.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 0 && $request_type == 'post') { $this->data_upload(); return; } - /** - *@OA\Get( - * path="/data/features/{id}", - * tags={"data"}, - * summary="Get data features", - * description="Returns the features of a dataset.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="All the features of the dataset", - * @OA\JsonContent( - * ref="#/components/schemas/DataFeatures", - * example={ - * "data_features": { - * "feature": [ - * { - * "index": "0", - * "name": "sepallength", - * "data_type": "numeric", - * "is_target": "false", - * "is_ignore": "false", - * "is_row_identifier": "false" - * }, - * { - * "index": "1", - * "name": "sepalwidth", - * "data_type": "numeric", - * "is_target": "false", - * "is_ignore": "false", - * "is_row_identifier": "false" - * }, - * { - * "index": "2", - * "name": "petallength", - * "data_type": "numeric", - * "is_target": "false", - * "is_ignore": "false", - * "is_row_identifier": "false" - * }, - * { - * "index": "3", - * "name": "petalwidth", - * "data_type": "numeric", - * "is_target": "false", - * "is_ignore": "false", - * "is_row_identifier": "false" - * }, - * { - * "index": "4", - * "name": "class", - * "data_type": "nominal", - * "is_target": "true", - * "is_ignore": "false", - * "is_row_identifier": "false" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n270 - Please provide dataset ID.\n271 - Unknown dataset. Data set with the given data ID was not found (or is not shared with you).\n272 - No features found. The dataset did not contain any features, or we could not extract them.\n273 - Dataset not processed yet. The dataset was not processed yet, features are not yet available. Please wait for a few minutes.\n274 - Dataset processed with error. The feature extractor has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, please contact the API admins.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && $segments[0] == 'features' && is_numeric($segments[1]) && in_array($request_type, $getpost)) { $this->data_features($segments[1]); return; } - /** - *@OA\Post( - * path="/data/features", - * tags={"data"}, - * summary="Upload dataset feature description", - * description="Uploads dataset feature description. Upon success, it returns the data id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.features) and an [XML example](https://www.openml.org/api/v1/xml_example/data.features).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n431 - Dataset already processed\n432 - Please provide description xml\n433 - Problem validating uploaded description file\n434 - Could not find dataset\n436 - Something wrong with XML, check data id and evaluation engine id\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'features' && $request_type == 'post') { $this->data_features_upload($segments[0]); return; } - /** - *@OA\Get( - * path="/data/qualities/list", - * tags={"data"}, - * summary="List all data qualities", - * description="Returns a list of all data qualities in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of data qualities", - * @OA\JsonContent( - * ref="#/components/schemas/DataQualityList", - * example={ - * "data_qualities_list":{ - * "quality":[ - * "NumberOfClasses", - * "NumberOfFeatures", - * "NumberOfInstances", - * "NumberOfInstancesWithMissingValues", - * "NumberOfMissingValues", - * "NumberOfNumericFeatures", - * "NumberOfSymbolicFeatures" - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned\n370 - No data qualities available. There are no data qualities in the system.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && $segments[0] == 'qualities' && $segments[1] == 'list' && in_array($request_type, $getpost)) { $this->data_qualities_list($segments[1]); return; } - /** - *@OA\Get( - * path="/data/qualities/{id}", - * tags={"data"}, - * summary="Get data qualities", - * description="Returns the qualities of a dataset.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="All the qualities of the dataset", - * @OA\JsonContent( - * ref="#/components/schemas/DataQualities", - * example={ - * "data_qualities": { - * "quality": [ - * { - * "name": "ClassCount", - * "value": "3.0" - * }, - * { - * "name": "ClassEntropy", - * "value": "1.584962500721156" - * }, - * { - * "name": "NumberOfClasses", - * "value": "3" - * }, - * { - * "name": "NumberOfFeatures", - * "value": "5" - * }, - * { - * "name": "NumberOfInstances", - * "value": "150" - * }, - * { - * "name": "NumberOfInstancesWithMissingValues", - * "value": "0" - * }, - * { - * "name": "NumberOfMissingValues", - * "value": "0" - * }, - * { - * "name": "NumberOfNumericFeatures", - * "value": "4" - * }, - * { - * "name": "NumberOfSymbolicFeatures", - * "value": "0" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n360 - Please provide data set ID\n361 - Unknown dataset. The data set with the given ID was not found in the database, or is not shared with you.\n362 - No qualities found. The registered dataset did not contain any calculated qualities.\n363 - Dataset not processed yet. The dataset was not processed yet, no qualities are available. Please wait for a few minutes.\n364 - Dataset processed with error. The quality calculator has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, contact the support team.\n365 - Interval start or end illegal. There was a problem with the interval\nstart or end.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && $segments[0] == 'qualities' && is_numeric($segments[1]) && in_array($request_type, $getpost)) { $this->data_qualities($segments[1], $this->config->item('default_evaluation_engine_id')); return; @@ -703,152 +116,18 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Post( - * path="/data/qualities", - * tags={"data"}, - * summary="Upload dataset qualities", - * description="Uploads dataset qualities. Upon success, it returns the data id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.qualities) and an [XML example](https://www.openml.org/api/v1/xml_example/data.qualities).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n381 - Something wrong with XML, please check did and evaluation_engine_id\n382 - Please provide description xml\n383 - Problem validating uploaded description file\n384 - Dataset not processed yet\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'qualities' && $request_type == 'post') { $this->data_qualities_upload($segments[0]); return; } - /** - *@OA\Post( - * path="/data/tag", - * tags={"data"}, - * summary="Tag a dataset", - * description="Tags a dataset.", - * @OA\Parameter( - * name="data_id", - * in="formData", - * type="number", - * format="integer", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the tagged dataset", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="data_tag", - * ref="#/components/schemas/inline_response_200_2_data_tag", - * ), - * example={ - * "data_tag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { - $this->entity_tag_untag('dataset', $this->input->post('data_id'), $this->input->post('tag'), false, 'data'); - return; - } - - /** - *@OA\Post( - * path="/data/untag", - * tags={"data"}, - * summary="Untag a dataset", - * description="Untags a dataset.", - * @OA\Parameter( - * name="data_id", - * in="formData", - * type="number", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The ID of the untagged dataset", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="data_untag", - * ref="#/components/schemas/inline_response_200_3_data_untag", - * ), - * example={ - * "data_untag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ + $this->data_tag($this->input->post('data_id'), $this->input->post('tag')); + return; + } + if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { - $this->entity_tag_untag('dataset', $this->input->post('data_id'), $this->input->post('tag'), true, 'data'); + $this->data_untag($this->input->post('data_id'), $this->input->post('tag')); return; } @@ -857,43 +136,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Post( - * path="/data/status/update/", - * tags={"data"}, - * summary="Change the status of a dataset", - * description="Change the status of a dataset, either 'active' or 'deactivated'", - * @OA\Parameter( - * name="data_id", - * in="formData", - * type="number", - * format="integer", - * description="Id of the dataset.", - * required="true", - * ), - * @OA\Parameter( - * name="status", - * in="formData", - * type="string", - * description="The status on which to filter the results, either 'active' or 'deactivated'.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n691 - Illegal status\n692 - Dataset does not exists\n693 - Dataset is not owned by you\n694 - Illegal status transition\n695 - Status update failed\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && $segments[0] == 'status' && $segments[1] == 'update') { $this->status_update($this->input->post('data_id'), $this->input->post('status')); return; @@ -902,6 +144,223 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError(100, $this->version); } + /** + *@OA\Post( + * path="/data/tag", + * tags={"data"}, + * summary="Tag a dataset", + * description="Tags a dataset.", + * @OA\Parameter( + * name="data_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_tag", + * ref="#/components/schemas/inline_response_200_2_data_tag", + * ), + * example={ + * "data_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function data_tag($data_id, $tag) { + $this->data_tag_untag($data_id, $tag, false); + } + + /** + *@OA\Post( + * path="/data/untag", + * tags={"data"}, + * summary="Untag a dataset", + * description="Untags a dataset.", + * @OA\Parameter( + * name="data_id", + * in="formData", + * type="number", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The ID of the untagged dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_untag", + * ref="#/components/schemas/inline_response_200_3_data_untag", + * ), + * example={ + * "data_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function data_untag($data_id, $tag) { + $this->data_tag_untag($data_id, $tag, true); + } + + private function data_tag_untag($data_id,$tag, $do_untag) { + // forward action to superclass + $this->entity_tag_untag('dataset', $data_id, $tag, $do_untag, 'data'); + } + + /** + *@OA\Get( + * path="/data/list/{filters}", + * tags={"data"}, + * summary="List and filter datasets", + * description="List datasets, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/data/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all datasets that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + /status/{status} - returns only datasets with a given status, either 'active', 'deactivated', or 'in_preparation'. + /tag/{tag} - returns only datasets tagged with the given tag. + /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, data_version, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'. + ", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of datasets with the given task", + * @OA\JsonContent( + * ref="#/components/schemas/DataList", + * example={ + * "data": { + * "dataset": [ + * { + * "did":"1", + * "name":"anneal", + * "status":"active", + * "format":"ARFF", + * "quality":[ + * { + * "name":"MajorityClassSize", + * "value":"684" + * }, + * { + * "name":"MaxNominalAttDistinctValues", + * "value":"10.0" + * }, + * { + * "name":"MinorityClassSize" + * ,"value":"0" + * }, + * { + * "name":"NumBinaryAtts", + * "value":"14.0" + * }, + * { + * "name":"NumberOfClasses", + * "value":"6" + * }, + * { + * "name":"NumberOfFeatures", + * "value":"39" + * }, + * { + * "name":"NumberOfInstances", + * "value":"898" + * }, + * { + * "name":"NumberOfInstancesWithMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfNumericFeatures", + * "value":"6" + * }, + * { + * "name":"NumberOfSymbolicFeatures", + * "value":"32" + * } + * ] + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n370 - Illegal filter specified.\n371 - Filter values/ranges not properly specified.\n372 - No results. There where no matches for the given constraints.\n373 - Can not specify an offset without a limit.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_list($segs) { $legal_filters = array('tag', 'status', 'limit', 'offset', 'data_id', 'data_name', 'data_version', 'uploader', 'number_instances', 'number_features', 'number_classes', 'number_missing_values'); @@ -991,6 +450,66 @@ private function data_list($segs) { $this->xmlContents('data', $this->version, array('datasets' => $datasets)); } + /** + *@OA\Get( + * path="/data/{id}", + * tags={"data"}, + * summary="Get dataset description", + * description="Returns information about a dataset. The information includes the name, information about the creator, URL to download it and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A dataset description", + * @OA\JsonContent( + * ref="#/components/schemas/Data", + * example={ + * "data_set_description": { + * "id": "1", + * "name": "anneal", + * "version": "2", + * "description": "...", + * "format": "ARFF", + * "upload_date": "2014-04-06 23:19:20", + * "licence": "Public", + * "url": "https://www.openml.org/data/download/1/dataset_1_anneal.arff", + * "file_id": "1", + * "default_target_attribute": "class", + * "version_label": "2", + * "tag": [ + * "study_1", + * "uci" + * ], + * "visibility": "public", + * "original_data_url": "https://www.openml.org/d/2", + * "status": "active", + * "md5_checksum": "d01f6ccd68c88b749b20bbe897de3713" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned\n110 - Please provide data_id.\n111 - Unknown dataset. Data set description with data_id was not found in the database.\n112 - No access granted. This dataset is not shared with you.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data($data_id) { if( $data_id == false ) { $this->returnError( 110, $this->version ); @@ -1070,6 +589,52 @@ private function data_reset($data_id) { $this->xmlContents('data-reset', $this->version, array('dataset' => $dataset)); } + /** + *@OA\Delete( + * path="/data/{id}", + * tags={"data"}, + * summary="Delete dataset", + * description="Deletes a dataset. Upon success, it returns the ID of the deleted dataset.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_delete", + * ref="#/components/schemas/inline_response_200_data_delete", + * ), + * example={ + * "data_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned\n- 350 - Please provide API key. In order to remove your content, please authenticate.\n- 351 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n- 352 - Dataset does not exists. The data ID could not be linked to an existing dataset.\n- 353 - Dataset is not owned by you. The dataset is owned by another user. Hence you cannot delete it.\n- 354 - Dataset is in use by other content. Can not be deleted. The data is used in tasks or runs. Delete other content before deleting this dataset.\n- 355 - Deleting dataset failed. Deleting the dataset failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_delete($data_id) { $dataset = $this->Dataset->getById( $data_id ); @@ -1118,6 +683,58 @@ private function data_delete($data_id) { } + /** + *@OA\Post( + * path="/data", + * tags={"data"}, + * summary="Upload dataset", + * description="Uploads a dataset. Upon success, it returns the data id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/data).", + * required=true, + * ), + * @OA\Parameter( + * name="dataset", + * in="formData", + * type="file", + * description="The actual dataset, being an ARFF file.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded dataset", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_data_set", + * ref="#/components/schemas/inline_response_200_1_upload_data_set", + * ), + * example={ + * "upload_data_set": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n130 - Problem with file uploading. There was a problem with the file upload.\n131 - Problem validating uploaded description file. The XML description format does not meet the standards.\n132 - Failed to move the files. Internal server error, please contact API administrators.\n133 - Failed to make checksum of datafile. Internal server error, please contact API administrators.\n134 - Failed to insert record in database. Internal server error, please contact API administrators.\n135 - Please provide description xml.\n136 - File failed format verification. The uploaded file is not valid according to the selected file format. Please check the file format specification and try again.\n137 - Please provide API key. In order to share content, please log in or provide your API key.\n138 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n139 - Combination name / version already exists. Leave version out for auto increment\n140 - Both dataset file and dataset url provided. The system is confused since both a dataset file (post) and a dataset url (xml) are provided. Please remove one.\n141 - Neither dataset file or dataset url are provided. Please provide either a dataset file as POST variable, or a dataset url in the description XML.\n142 - Error in processing arff file. Can be a syntax error, or the specified target feature does not exists. For now, we only check on arff files. If a dataset is claimed to be in such a format, and it can not be parsed, this error is returned.\n143 - Suggested target feature not legal. It is possible to suggest a default target feature (for predictive tasks). However, it should be provided in the data.\n144 - Unable to update dataset. The dataset with id could not be found in the database. If you upload a new dataset, unset the id.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_upload() { // get correct description $xsdFile = xsd('openml.data.upload', $this->controller, $this->version); @@ -1297,7 +914,44 @@ private function data_upload() { // create $this->xmlContents('data-upload', $this->version, array('id' => $id)); } - + + /** + *@OA\Post( + * path="/data/status/update/", + * tags={"data"}, + * summary="Change the status of a dataset", + * description="Change the status of a dataset, either 'active' or 'deactivated'", + * @OA\Parameter( + * name="data_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="status", + * in="formData", + * type="string", + * description="The status on which to filter the results, either 'active' or 'deactivated'.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n691 - Illegal status\n692 - Dataset does not exists\n693 - Dataset is not owned by you\n694 - Illegal status transition\n695 - Status update failed\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function status_update($data_id, $status) { // in_preparation is not a legal status to change to $legal_status = array('active', 'deactivated'); @@ -1365,6 +1019,89 @@ private function status_update($data_id, $status) { $this->xmlContents('data-status-update', $this->version, array('did' => $data_id, 'status' => $status)); } + /** + *@OA\Get( + * path="/data/features/{id}", + * tags={"data"}, + * summary="Get data features", + * description="Returns the features of a dataset.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="All the features of the dataset", + * @OA\JsonContent( + * ref="#/components/schemas/DataFeatures", + * example={ + * "data_features": { + * "feature": [ + * { + * "index": "0", + * "name": "sepallength", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "1", + * "name": "sepalwidth", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "2", + * "name": "petallength", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "3", + * "name": "petalwidth", + * "data_type": "numeric", + * "is_target": "false", + * "is_ignore": "false", + * "is_row_identifier": "false" + * }, + * { + * "index": "4", + * "name": "class", + * "data_type": "nominal", + * "is_target": "true", + * "is_ignore": "false", + * "is_row_identifier": "false" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n270 - Please provide dataset ID.\n271 - Unknown dataset. Data set with the given data ID was not found (or is not shared with you).\n272 - No features found. The dataset did not contain any features, or we could not extract them.\n273 - Dataset not processed yet. The dataset was not processed yet, features are not yet available. Please wait for a few minutes.\n274 - Dataset processed with error. The feature extractor has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, please contact the API admins.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_features($data_id) { $dataset = $this->Dataset->getById($data_id); if ($dataset === false) { @@ -1418,6 +1155,35 @@ private function data_features($data_id) { $this->xmlContents('data-features', $this->version, $dataset); } + /** + *@OA\Post( + * path="/data/features", + * tags={"data"}, + * summary="Upload dataset feature description", + * description="Uploads dataset feature description. Upon success, it returns the data id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.features) and an [XML example](https://www.openml.org/api/v1/xml_example/data.features).", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n431 - Dataset already processed\n432 - Please provide description xml\n433 - Problem validating uploaded description file\n434 - Could not find dataset\n436 - Something wrong with XML, check data id and evaluation engine id\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_features_upload() { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); @@ -1596,6 +1362,48 @@ private function data_features_upload() { } } + /** + *@OA\Get( + * path="/data/qualities/list", + * tags={"data"}, + * summary="List all data qualities", + * description="Returns a list of all data qualities in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of data qualities", + * @OA\JsonContent( + * ref="#/components/schemas/DataQualityList", + * example={ + * "data_qualities_list":{ + * "quality":[ + * "NumberOfClasses", + * "NumberOfFeatures", + * "NumberOfInstances", + * "NumberOfInstancesWithMissingValues", + * "NumberOfMissingValues", + * "NumberOfNumericFeatures", + * "NumberOfSymbolicFeatures" + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned\n370 - No data qualities available. There are no data qualities in the system.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_qualities_list() { $result = $this->Quality->allUsed( ); $qualities = array(); @@ -1626,6 +1434,85 @@ private function feature_qualities_list() { } + /** + *@OA\Get( + * path="/data/qualities/{id}", + * tags={"data"}, + * summary="Get data qualities", + * description="Returns the qualities of a dataset.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the dataset.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="All the qualities of the dataset", + * @OA\JsonContent( + * ref="#/components/schemas/DataQualities", + * example={ + * "data_qualities": { + * "quality": [ + * { + * "name": "ClassCount", + * "value": "3.0" + * }, + * { + * "name": "ClassEntropy", + * "value": "1.584962500721156" + * }, + * { + * "name": "NumberOfClasses", + * "value": "3" + * }, + * { + * "name": "NumberOfFeatures", + * "value": "5" + * }, + * { + * "name": "NumberOfInstances", + * "value": "150" + * }, + * { + * "name": "NumberOfInstancesWithMissingValues", + * "value": "0" + * }, + * { + * "name": "NumberOfMissingValues", + * "value": "0" + * }, + * { + * "name": "NumberOfNumericFeatures", + * "value": "4" + * }, + * { + * "name": "NumberOfSymbolicFeatures", + * "value": "0" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n360 - Please provide data set ID\n361 - Unknown dataset. The data set with the given ID was not found in the database, or is not shared with you.\n362 - No qualities found. The registered dataset did not contain any calculated qualities.\n363 - Dataset not processed yet. The dataset was not processed yet, no qualities are available. Please wait for a few minutes.\n364 - Dataset processed with error. The quality calculator has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, contact the support team.\n365 - Interval start or end illegal. There was a problem with the interval\nstart or end.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_qualities($data_id, $evaluation_engine_id) { if( $data_id == false ) { $this->returnError( 360, $this->version ); @@ -1737,6 +1624,35 @@ private function feature_qualities($data_id, $evaluation_engine_id) { $this->xmlContents( 'feature-qualities', $this->version, $dataset ); } + /** + *@OA\Post( + * path="/data/qualities", + * tags={"data"}, + * summary="Upload dataset qualities", + * description="Uploads dataset qualities. Upon success, it returns the data id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.qualities) and an [XML example](https://www.openml.org/api/v1/xml_example/data.qualities).", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n381 - Something wrong with XML, please check did and evaluation_engine_id\n382 - Please provide description xml\n383 - Problem validating uploaded description file\n384 - Dataset not processed yet\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_qualities_upload() { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); @@ -1863,6 +1779,50 @@ private function data_qualities_upload() { } } + /** + *@OA\Get( + * path="/data/unprocessed/{data_engine_id}/{order}", + * tags={"data"}, + * summary="Get a list of unprocessed datasets", + * description="This call is for people running their own dataset processing engines. It returns the details of datasets that are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", + * @OA\Parameter( + * name="data_engine_id", + * in="path", + * type="string", + * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", + * required=true, + * ), + * @OA\Parameter( + * name="order", + * in="path", + * type="string", + * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of unprocessed datasets", + * @OA\JsonContent( + * ref="#/components/schemas/DataUnprocessed", + * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n681 - No unprocessed datasets.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function data_unprocessed($evaluation_engine_id, $order) { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); @@ -1903,6 +1863,57 @@ private function data_unprocessed($evaluation_engine_id, $order) { $this->xmlContents('data-unprocessed', $this->version, array('res' => $result)); } + /** + *@OA\Post( + * path="/data/qualities/unprocessed/{data_engine_id}/{order}", + * tags={"data"}, + * summary="Get a list of datasets with unprocessed qualities", + * description="This call is for people running their own dataset processing engines. It returns the details of datasets for which certain qualities are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", + * @OA\Parameter( + * name="data_engine_id", + * in="path", + * type="string", + * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", + * required=true, + * ), + * @OA\Parameter( + * name="order", + * in="path", + * type="string", + * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Parameter( + * name="qualities", + * in="formData", + * type="string", + * description="Comma-separated list of (at least two) quality names, e.g. 'NumberOfInstances,NumberOfFeatures'.", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="A list of unprocessed datasets", + * @OA\JsonContent( + * ref="#/components/schemas/DataUnprocessed", + * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n686 - Please specify the features the evaluation engine wants to calculate (at least 2).\n687 - No unprocessed datasets according to the given set of meta-features.\n688 - Illegal qualities.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function dataqualities_unprocessed($evaluation_engine_id, $order, $feature_attributes = false, $priorityTag = null) { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); diff --git a/openml_OS/models/api/v1/Api_estimationprocedure.php b/openml_OS/models/api/v1/Api_estimationprocedure.php index a82d1b8e3..30012c80c 100644 --- a/openml_OS/models/api/v1/Api_estimationprocedure.php +++ b/openml_OS/models/api/v1/Api_estimationprocedure.php @@ -13,59 +13,6 @@ function __construct() { function bootstrap($format, $segments, $request_type, $user_id) { $this->outputFormat = $format; - /** - *@OA\Get( - * path="/estimationprocedure/list", - * tags={"estimationprocedure"}, - * summary="List all estimation procedures", - * description="Returns an array with all model performance estimation procedures in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of estimation procedures", - * @OA\JsonContent( - * ref="#/components/schemas/EstimationProcedureList", - * example={ - * "estimationprocedures": { - * "estimationprocedure": [ - * { - * "id":"1", - * "ttid":"1", - * "name":"10-fold Crossvalidation", - * "type":"crossvalidation", - * "repeats":"1", - * "folds":"10", - * "stratified_sampling":"true" - * }, - * { - * "id":"2", - * "ttid":"1", - * "name":"5 times 2-fold Crossvalidation", - * "type":"crossvalidation", - * "repeats":"5", - * "folds":"2", - * "stratified_sampling":"true" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n500 - No model performance estimation procedures available.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'list') { $this->estimationprocedure_list(); return; @@ -95,8 +42,60 @@ private function estimationprocedure($id) { $this->xmlContents( 'estimationprocedure-get', $this->version, array( 'ep' => $ep ) ); } + /** + *@OA\Get( + * path="/estimationprocedure/list", + * tags={"estimationprocedure"}, + * summary="List all estimation procedures", + * description="Returns an array with all model performance estimation procedures in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of estimation procedures", + * @OA\JsonContent( + * ref="#/components/schemas/EstimationProcedureList", + * example={ + * "estimationprocedures": { + * "estimationprocedure": [ + * { + * "id":"1", + * "ttid":"1", + * "name":"10-fold Crossvalidation", + * "type":"crossvalidation", + * "repeats":"1", + * "folds":"10", + * "stratified_sampling":"true" + * }, + * { + * "id":"2", + * "ttid":"1", + * "name":"5 times 2-fold Crossvalidation", + * "type":"crossvalidation", + * "repeats":"5", + * "folds":"2", + * "stratified_sampling":"true" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n500 - No model performance estimation procedures available.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function estimationprocedure_list() { - $estimationprocedures = $this->Estimation_procedure->get(); if( $estimationprocedures == false ) { $this->returnError( 500, $this->version ); diff --git a/openml_OS/models/api/v1/Api_evaluation.php b/openml_OS/models/api/v1/Api_evaluation.php index 12a4eb10a..58b21c419 100644 --- a/openml_OS/models/api/v1/Api_evaluation.php +++ b/openml_OS/models/api/v1/Api_evaluation.php @@ -20,56 +20,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { array_shift($segments); $this->evaluation_list($segments, $user_id, true); return; - } - /** - *@OA\Get( - * path="/evaluation/list/{filters}", - * tags={"evaluation"}, - * summary="List and filter evaluations", - * description="List evaluations, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/evaluation/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, uploaders or runs.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /function/{name} - name of the evaluation measure, e.g. area_under_auc or predictive_accuracy. See the OpenML website for the complete list of measures. - /tag/{tag} - returns only evaluations of runs tagged with the given tag. - /run/{ids} - return only evaluations for specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3'' - /task/{ids} - return only evaluations for specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3'' - /flow/{ids} - return only evaluations for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' - /setup/{ids} - return only evaluations for specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' - /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. - /per_fold/{true,false} - whether or not to return crossvalidation scores per fold. Defaults to 'false'. Setting it to 'true' leads to large numbers of results, use only for very specific sets of runs. - /sort_order/{asc,desc} - sorts the results by the evaluation value, according to the selected evaluation measure (function) - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of evaluations descriptions", - * @OA\JsonContent( - * ref="#/components/schemas/EvaluationList", - * example={"evaluations": {"evaluation": [{"function": "area_under_roc_curve", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]", "value": "0.839359", "uploader": "1", "flow_id": "61"}, {"function": "f_measure", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]", "value": "0.600026", "uploader": "1", "flow_id": "61"}, {"function": "predictive_accuracy", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": [], "value": "0.614634", "uploader": "1", "flow_id": "61"}]}} - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n540 - Please provide at least task, flow or setup, uploader or run, to\nfilter results, or limit the number of responses.\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n542 - There where no results. Check whether there are runs under the given constraint.\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n544 - Illegal filter specified.\n545 - Offset specified without limit.\n546 - Requested result limit too high.\n547 - Per fold can only be set to value "true" or "false".\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ - elseif (count($segments) >= 1 && $segments[0] == 'list') { + } elseif (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->evaluation_list($segments, $user_id, false); return; @@ -77,50 +28,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { $order_values = array('random', 'reverse', 'normal'); - /** - *@OA\Get( - * path="/evaluation/request/{evaluation_engine_id}/{order}", - * tags={"evaluation"}, - * summary="Get an unevaluated run", - * description="This call is for people running their own evaluation engines. It returns the details of a run that is not yet evaluated by the given evaluation engine. It doesn't evaluate the run, it just returns the run info.", - * @OA\Parameter( - * name="evaluation_engine_id", - * in="path", - * type="string", - * description="The ID of the evaluation engine. You get this ID when you register a new evaluation engine with OpenML. The ID of the main evaluation engine is 1.", - * required="true", - * ), - * @OA\Parameter( - * name="order", - * in="path", - * type="string", - * description="When there are multiple runs still to evaluate, this defines which one to return. Options are 'normal' - the oldest run, 'reverse' - the newest run, or 'random' - a random run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of evaluations descriptions", - * @OA\JsonContent( - * ref="#/components/schemas/EvaluationRequest", - * example={"evaluation_request": {"run": [{"setup_id": "68799271", "upload_time": "2018-04-03 21:05:38", "uploader": "1935", "task_id": "3021", "run_id": "8943712"}]}} - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n100 - Function not valid.\n545 - No unevaluated runs according to the criteria.\n546 - Illegal filter.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) >= 4 && $segments[0] == 'request' && is_numeric($segments[1]) && in_array($segments[2], $order_values) && is_numeric($segments[3])) { array_shift($segments); // removes 'request' $eval_id = array_shift($segments); @@ -134,6 +41,50 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError(100, $this->version); } + /** + *@OA\Get( + * path="/evaluation/request/{evaluation_engine_id}/{order}", + * tags={"evaluation"}, + * summary="Get an unevaluated run", + * description="This call is for people running their own evaluation engines. It returns the details of a run that is not yet evaluated by the given evaluation engine. It doesn't evaluate the run, it just returns the run info.", + * @OA\Parameter( + * name="evaluation_engine_id", + * in="path", + * type="string", + * description="The ID of the evaluation engine. You get this ID when you register a new evaluation engine with OpenML. The ID of the main evaluation engine is 1.", + * required=true, + * ), + * @OA\Parameter( + * name="order", + * in="path", + * type="string", + * description="When there are multiple runs still to evaluate, this defines which one to return. Options are 'normal' - the oldest run, 'reverse' - the newest run, or 'random' - a random run.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of evaluations descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationRequest", + * example={"evaluation_request": {"run": [{"setup_id": "68799271", "upload_time": "2018-04-03 21:05:38", "uploader": "1935", "task_id": "3021", "run_id": "8943712"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n100 - Function not valid.\n545 - No unevaluated runs according to the criteria.\n546 - Illegal filter.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function evaluation_request($evaluation_engine_id, $order, $num_requests, $segs) { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); @@ -188,6 +139,54 @@ private function evaluation_request($evaluation_engine_id, $order, $num_request } + /** + *@OA\Get( + * path="/evaluation/list/{filters}", + * tags={"evaluation"}, + * summary="List and filter evaluations", + * description="List evaluations, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/evaluation/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, uploaders or runs.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /function/{name} - name of the evaluation measure, e.g. area_under_auc or predictive_accuracy. See the OpenML website for the complete list of measures. + /tag/{tag} - returns only evaluations of runs tagged with the given tag. + /run/{ids} - return only evaluations for specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3'' + /task/{ids} - return only evaluations for specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3'' + /flow/{ids} - return only evaluations for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' + /setup/{ids} - return only evaluations for specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' + /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + /per_fold/{true,false} - whether or not to return crossvalidation scores per fold. Defaults to 'false'. Setting it to 'true' leads to large numbers of results, use only for very specific sets of runs. + /sort_order/{asc,desc} - sorts the results by the evaluation value, according to the selected evaluation measure (function) + ", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of evaluations descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationList", + * example={"evaluations": {"evaluation": [{"function": "area_under_roc_curve", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]", "value": "0.839359", "uploader": "1", "flow_id": "61"}, {"function": "f_measure", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]", "value": "0.600026", "uploader": "1", "flow_id": "61"}, {"function": "predictive_accuracy", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": [], "value": "0.614634", "uploader": "1", "flow_id": "61"}]}} + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n540 - Please provide at least task, flow or setup, uploader or run, to\nfilter results, or limit the number of responses.\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n542 - There where no results. Check whether there are runs under the given constraint.\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n544 - Illegal filter specified.\n545 - Offset specified without limit.\n546 - Requested result limit too high.\n547 - Per fold can only be set to value "true" or "false".\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function evaluation_list($segs, $user_id) { $result_limit = 10000; $legal_filters = array('task', 'setup', 'flow', 'uploader', 'run', 'tag', 'limit', 'offset', 'function', 'per_fold', 'sort_order', 'study'); diff --git a/openml_OS/models/api/v1/Api_evaluationmeasure.php b/openml_OS/models/api/v1/Api_evaluationmeasure.php index 56d01ee05..e554ea3e2 100644 --- a/openml_OS/models/api/v1/Api_evaluationmeasure.php +++ b/openml_OS/models/api/v1/Api_evaluationmeasure.php @@ -16,40 +16,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->outputFormat = $format; - /** - *@OA\Get( - * path="/evaluationmeasure/list", - * tags={"evaluationmeasure"}, - * summary="List all evaluation measures", - * description="Returns an array with all model evaluation measures in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of evaluation measures", - * @OA\JsonContent( - * ref="#/components/schemas/EvaluationMeasureList", - * example={ - * "evaluation_measures":{ - * "measures":{ - * "measure":[ - * "area_under_roc_curve", - * "average_cost", - * "binominal_test", - * "build_cpu_time" - * ] - * } - * } - * } - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'list') { $this->evaluationmeasure_list(); return; @@ -59,7 +25,40 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } - + /** + *@OA\Get( + * path="/evaluationmeasure/list", + * tags={"evaluationmeasure"}, + * summary="List all evaluation measures", + * description="Returns an array with all model evaluation measures in the system.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of evaluation measures", + * @OA\JsonContent( + * ref="#/components/schemas/EvaluationMeasureList", + * example={ + * "evaluation_measures":{ + * "measures":{ + * "measure":[ + * "area_under_roc_curve", + * "average_cost", + * "binominal_test", + * "build_cpu_time" + * ] + * } + * } + * } + * ), + * ), + *) + */ private function evaluationmeasure_list() { $data = new stdClass(); $data->measures = $this->Math_function->getWhere( 'functionType = "EvaluationFunction"' ); diff --git a/openml_OS/models/api/v1/Api_flow.php b/openml_OS/models/api/v1/Api_flow.php index 3a86f629d..cd6b1a31f 100644 --- a/openml_OS/models/api/v1/Api_flow.php +++ b/openml_OS/models/api/v1/Api_flow.php @@ -28,77 +28,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); - /** - *@OA\Get( - * path="/flow/list/{filters}", - * tags={"flow"}, - * summary="List and filter flows", - * description="List flows, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all flows that match the constraints.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. - /tag/{tag} - returns only tasks tagged with the given tag. - /uploader/{id} - return only evaluations uploaded by a specific user, specified by user ID. - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of flows", - * @OA\JsonContent( - * ref="#/components/schemas/FlowList", - * example={ - * "flows": - * { - * "flow":[ - * { - * "id":"65", - * "full_name":"weka.RandomForest(1)", - * "name":"weka.RandomForest", - * "version":"1", - * "external_version":"Weka_3.7.10_9186", - * "uploader":"1" - * }, - * { - * "id":"66", - * "full_name":"weka.IBk(1)", - * "name":"weka.IBk", - * "version":"1", - * "external_version":"Weka_3.7.10_8034", - * "uploader":"1" - * }, - * { - * "id":"67", - * "full_name":"weka.BayesNet_K2(1)", - * "name":"weka.BayesNet_K2", - * "version":"1", - * "external_version":"Weka_3.7.10_8034", - * "uploader":"1" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n500 - No results. There where no matches for the given constraints.\n501 - Illegal filter specified.\n502 - Filter values/ranges not properly specified.\n503 - Can not specify an offset without a limit.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->flow_list($segments); @@ -106,59 +35,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { } // TODO: deprecate! - /** - *@OA\Get( - * path="/flow/exists/{name}/{version}", - * tags={"flow"}, - * summary="Check whether flow exists", - * description="Checks whether a flow with the given name and (external) version exists.", - * @OA\Parameter( - * name="name", - * in="path", - * type="string", - * description="The name of the flow.", - * required="true", - * ), - * @OA\Parameter( - * name="version", - * in="path", - * type="string", - * description="The external version of the flow", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of flows", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_exists", - * ref="#/components/schemas/inline_response_200_10_flow_exists", - * ), - * example={ - * "flow_exists": { - * "exists": "true", - * "id": "65" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n330 - Mandatory fields not present. Please provide name and external_version.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 3 && $segments[0] == 'exists') { $this->flow_exists($segments[1],$segments[2]); return; @@ -169,121 +45,11 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Get( - * path="/flow/{id}", - * tags={"flow"}, - * summary="Get flow description", - * description="Returns information about a flow. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the flow.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A flow description", - * @OA\JsonContent( - * ref="#/components/schemas/Flow", - * example={ - * "flow": { - * "id":"100", - * "uploader":"1", - * "name":"weka.J48", - * "version":"2", - * "external_version":"Weka_3.7.5_9117", - * "description":"...", - * "upload_date":"2014-04-23 18:00:36", - * "language":"Java", - * "dependencies":"Weka_3.7.5", - * "parameter": [ - * { - * "name":"A", - * "data_type":"flag", - * "default_value":[], - * "description":"Laplace smoothing..." - * }, - * { - * "name":"C", - * "data_type":"option", - * "default_value":"0.25", - * "description":"Set confidence threshold..." - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n180 - Please provide flow id.\n181 - Unknown flow. The flow with the given ID was not found in the database.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->flow($segments[0]); return; } - /** - *@OA\Delete( - * path="/flow/{id}", - * tags={"flow"}, - * summary="Delete a flow", - * description="Deletes a flow. Upon success, it returns the ID of the deleted flow.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the flow.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted flow", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_delete", - * ref="#/components/schemas/inline_response_200_8_flow_delete", - * ), - * example={ - * "flow_delete": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n320 - Please provide API key. In order to remove your content, please authenticate.\n321 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n322 - Flow does not exists. The flow ID could not be linked to an existing flow.\n323 - Flow is not owned by you. The flow is owned by another user. Hence you cannot delete it.\n324 - Flow is in use by other content. Can not be deleted. The flow is used in runs, evaluations or as a component of another flow. Delete other content before deleting this flow.\n325 - Deleting flow failed. Deleting the flow failed. Please contact\nsupport team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->flow_delete($segments[0]); return; @@ -294,175 +60,18 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Post( - * path="/flow", - * tags={"flow"}, - * summary="Upload a flow", - * description="Uploads a flow. Upon success, it returns the flow id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", - * required="true", - * ), - * @OA\Parameter( - * name="flow", - * in="formData", - * type="file", - * description="The actual flow, being a source (or binary) file.", - * required="false", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded flow", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_9_upload_flow", - * ), - * example={ - * "upload_flow": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n160 - Error in file uploading. There was a problem with the file upload.\n161 - Please provide description xml.\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\n164 - Flow already stored in database. Please change name or version number\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\n166 - Failed to add flow to database. Internal server error, please contact API administrators\n167 - Illegal files uploaded. An non required file was uploaded.\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n171 - Flow already exists. This flow is already in the database\n172 - XSD not found. Please contact API support team\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 0 && $request_type == 'post') { $this->flow_upload(); return; } - /** - *@OA\Post( - * path="/flow/tag", - * tags={"flow"}, - * summary="Tag a flow", - * description="Tags a flow.", - * @OA\Parameter( - * name="flow_id", - * in="formData", - * type="number", - * format="integer", - * description="Id of the flow.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the tagged flow", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_tag", - * ref="#/components/schemas/inline_response_200_12_flow_tag", - * ), - * example={ - * "flow_tag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { - $this->entity_tag_untag('implementation', $this->input->post('flow_id'), $this->input->post('tag'), false, 'flow'); + $this->flow_tag($this->input->post('flow_id'), $this->input->post('tag')); return; } - /** - *@OA\Post( - * path="/flow/untag", - * tags={"flow"}, - * summary="Untag a flow", - * description="Untags a flow.", - * @OA\Parameter( - * name="flow_id", - * in="formData", - * type="number", - * description="Id of the flow.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the untagged flow", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_untag", - * ref="#/components/schemas/inline_response_200_13_flow_untag", - * ), - * example={ - * "flow_untag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { - $this->entity_tag_untag('implementation', $this->input->post('flow_id'), $this->input->post('tag'), true, 'flow'); + $this->flow_untag($this->input->post('flow_id'), $this->input->post('tag')); return; } @@ -474,6 +83,77 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } + /** + *@OA\Get( + * path="/flow/list/{filters}", + * tags={"flow"}, + * summary="List and filter flows", + * description="List flows, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all flows that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. + /tag/{tag} - returns only tasks tagged with the given tag. + /uploader/{id} - return only evaluations uploaded by a specific user, specified by user ID. + ", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of flows", + * @OA\JsonContent( + * ref="#/components/schemas/FlowList", + * example={ + * "flows": + * { + * "flow":[ + * { + * "id":"65", + * "full_name":"weka.RandomForest(1)", + * "name":"weka.RandomForest", + * "version":"1", + * "external_version":"Weka_3.7.10_9186", + * "uploader":"1" + * }, + * { + * "id":"66", + * "full_name":"weka.IBk(1)", + * "name":"weka.IBk", + * "version":"1", + * "external_version":"Weka_3.7.10_8034", + * "uploader":"1" + * }, + * { + * "id":"67", + * "full_name":"weka.BayesNet_K2(1)", + * "name":"weka.BayesNet_K2", + * "version":"1", + * "external_version":"Weka_3.7.10_8034", + * "uploader":"1" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n500 - No results. There where no matches for the given constraints.\n501 - Illegal filter specified.\n502 - Filter values/ranges not properly specified.\n503 - Can not specify an offset without a limit.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function flow_list($segs) { $legal_filters = array('uploader', 'tag', 'limit', 'offset'); @@ -527,7 +207,178 @@ private function flow_list($segs) { $this->xmlContents('implementations', $this->version, array('implementations' => $implementations_res)); } + /** + *@OA\Post( + * path="/flow/tag", + * tags={"flow"}, + * summary="Tag a flow", + * description="Tags a flow.", + * @OA\Parameter( + * name="flow_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the flow.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_tag", + * ref="#/components/schemas/inline_response_200_12_flow_tag", + * ), + * example={ + * "flow_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function flow_tag($flow_id, $tag) { + $this->flow_tag_untag($flow_id,$tag, false); + } + + /** + *@OA\Post( + * path="/flow/untag", + * tags={"flow"}, + * summary="Untag a flow", + * description="Untags a flow.", + * @OA\Parameter( + * name="flow_id", + * in="formData", + * type="number", + * description="Id of the flow.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the untagged flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_untag", + * ref="#/components/schemas/inline_response_200_13_flow_untag", + * ), + * example={ + * "flow_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function flow_untag($flow_id, $tag) { + $this->flow_tag_untag($flow_id, $tag, true); + } + + private function flow_tag_untag($flow_id, $tag, $do_untag) { + //forward execution of logic to superclass. + $this->entity_tag_untag('implementation', $flow_id, $tag, $do_untag, 'flow'); + } + + /** + *@OA\Get( + * path="/flow/exists/{name}/{version}", + * tags={"flow"}, + * summary="Check whether flow exists", + * description="Checks whether a flow with the given name and (external) version exists.", + * @OA\Parameter( + * name="name", + * in="path", + * type="string", + * description="The name of the flow.", + * required=true, + * ), + * @OA\Parameter( + * name="version", + * in="path", + * type="string", + * description="The external version of the flow", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of flows", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_exists", + * ref="#/components/schemas/inline_response_200_10_flow_exists", + * ), + * example={ + * "flow_exists": { + * "exists": "true", + * "id": "65" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n330 - Mandatory fields not present. Please provide name and external_version.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function flow_exists($name, $external_version) { $similar = false; @@ -545,6 +396,70 @@ private function flow_exists($name, $external_version) { $this->xmlContents( 'implementation-exists', $this->version, $result ); } + /** + *@OA\Get( + * path="/flow/{id}", + * tags={"flow"}, + * summary="Get flow description", + * description="Returns information about a flow. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the flow.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A flow description", + * @OA\JsonContent( + * ref="#/components/schemas/Flow", + * example={ + * "flow": { + * "id":"100", + * "uploader":"1", + * "name":"weka.J48", + * "version":"2", + * "external_version":"Weka_3.7.5_9117", + * "description":"...", + * "upload_date":"2014-04-23 18:00:36", + * "language":"Java", + * "dependencies":"Weka_3.7.5", + * "parameter": [ + * { + * "name":"A", + * "data_type":"flag", + * "default_value":[], + * "description":"Laplace smoothing..." + * }, + * { + * "name":"C", + * "data_type":"option", + * "default_value":"0.25", + * "description":"Set confidence threshold..." + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n180 - Please provide flow id.\n181 - Unknown flow. The flow with the given ID was not found in the database.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ // TODO: check what is going wrong with implementation id 1 private function flow($id) { if( $id == false ) { @@ -562,6 +477,58 @@ private function flow($id) { $this->xmlContents( 'implementation-get', $this->version, array( 'source' => $implementation ) ); } + /** + *@OA\Post( + * path="/flow", + * tags={"flow"}, + * summary="Upload a flow", + * description="Uploads a flow. Upon success, it returns the flow id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", + * required=true, + * ), + * @OA\Parameter( + * name="flow", + * in="formData", + * type="file", + * description="The actual flow, being a source (or binary) file.", + * required=false, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_9_upload_flow", + * ), + * example={ + * "upload_flow": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n160 - Error in file uploading. There was a problem with the file upload.\n161 - Please provide description xml.\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\n164 - Flow already stored in database. Please change name or version number\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\n166 - Failed to add flow to database. Internal server error, please contact API administrators\n167 - Illegal files uploaded. An non required file was uploaded.\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n171 - Flow already exists. This flow is already in the database\n172 - XSD not found. Please contact API support team\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function flow_upload() { if(isset($_FILES['source']) && $_FILES['source']['error'] == 0) { @@ -688,6 +655,52 @@ private function flow_upload() { $this->xmlContents( 'implementation-upload', $this->version, $implementation ); } + /** + *@OA\Delete( + * path="/flow/{id}", + * tags={"flow"}, + * summary="Delete a flow", + * description="Deletes a flow. Upon success, it returns the ID of the deleted flow.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the flow.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted flow", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_delete", + * ref="#/components/schemas/inline_response_200_8_flow_delete", + * ), + * example={ + * "flow_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n320 - Please provide API key. In order to remove your content, please authenticate.\n321 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n322 - Flow does not exists. The flow ID could not be linked to an existing flow.\n323 - Flow is not owned by you. The flow is owned by another user. Hence you cannot delete it.\n324 - Flow is in use by other content. Can not be deleted. The flow is used in runs, evaluations or as a component of another flow. Delete other content before deleting this flow.\n325 - Deleting flow failed. Deleting the flow failed. Please contact\nsupport team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function flow_delete($flow_id) { $implementation = $this->Implementation->getById($flow_id); diff --git a/openml_OS/models/api/v1/Api_run.php b/openml_OS/models/api/v1/Api_run.php index 6e728903b..85f91cf6e 100644 --- a/openml_OS/models/api/v1/Api_run.php +++ b/openml_OS/models/api/v1/Api_run.php @@ -50,588 +50,48 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Post( - * path="/run/evaluate", - * tags={"run"}, - * summary="Uploads a run evaluation", - * description="Uploads a run evaluation. When successful, it returns the run id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the run evaluation.Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.evaluate) and an [XML example](https://www.openml.org/api/v1/xml_example/run.evaluate).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the evaluated run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_21_upload_flow", - * ), - * example={ - * "run_evaluate": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n422 - Upload problem description XML\n423 - Problem validating uploaded description file\n424 - Problem opening description xml\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'evaluate' && $request_type == 'post') { $this->run_evaluate(); return; } - /** - *@OA\Post( - * path="/run/trace/{id}", - * tags={"run"}, - * summary="Upload run trace", - * description="Uploads a run trace. Upon success, it returns the run id.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the run with the trace", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_23_upload_flow", - * ), - * example={ - * "run_trace": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n561 - Problem with uploaded trace file.\n562 - Problem validating xml trace file.\n563 - Problem loading xml trace file.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'trace' && $request_type == 'post') { $this->run_trace_upload(); return; } - /** - *@OA\Get( - * path="/run/trace/{id}", - * tags={"run"}, - * summary="Get run trace", - * description="Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A run trace description", - * @OA\JsonContent( - * ref="#/components/schemas/RunTrace", - * example={ - * "trace": { - * "run_id":"573055", - * "trace_iteration": { - * "repeat":"0", - * "fold":"0", - * "repeat":"0", - * "iteration":"0", - * "setup_string":{"parameter_minNumObj": "1", - * "parameter_confidenceFactor": "0.1"}, - * "evaluation":"94.814815", - * "selected": "true" - * }, - * "trace_iteration": { - * "repeat":"0", - * "fold":"0", - * "repeat":"0", - * "iteration":"1", - * "setup_string":{"parameter_minNumObj": "1", - * "parameter_confidenceFactor": "0.25"}, - * "evaluation": "94.074074", - * "selected": "true" - * } - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n570 - No successful trace associated with this run\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && $segments[0] == 'trace' && is_numeric($segments[1])) { $this->run_trace($segments[1]); return; } - /** - *@OA\Get( - * path="/run/{id}", - * tags={"run"}, - * summary="Get run description", - * description="Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A run description", - * @OA\JsonContent( - * ref="#/components/schemas/Run", - * example={ - * "run": { - * "run_id":"100", - * "uploader":"1", - * "uploader_name":"Jan van Rijn", - * "task_id":"28", - * "task_type":"Supervised Classification", - * "task_evaluation_measure":"predictive_accuracy", - * "flow_id":"67", - * "flow_name":"weka.BayesNet_K2(1)", - * "setup_string":"weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", - * "parameter_setting": [ - * { - * "name":"D", - * "value":"true" - * }, - * { - * "name":"Q", - * "value":"weka.classifiers.bayes.net.search.local.K2" - * }, - * { - * "name":"P", - * "value":"1" - * }, - * { - * "name":"S", - * "value":"BAYES" - * } - * ], - * "input_data": - * { - * "dataset": - * { - * "did":"28", - * "name":"optdigits", - * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/28\\/dataset_28_optdigits.arff" - * } - * }, - * "output_data": - * { - * "file": [ - * { - * "did":"48838", - * "file_id":"261", - * "name":"description", - * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/261\\/weka_generated_run935374685998857626.xml" - * }, - * { - * "did":"48839", - * "file_id":"262", - * "name":"predictions", - * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff" - * } - * ], - * "evaluation": [ - * { - * "name":"area_under_roc_curve", - * "flow_id":"4", - * "value":"0.990288", - * "array_data":"[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]" - * }, - * { - * "name":"confusion_matrix", - * "flow_id":"10", - * "array_data":"[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]" - * }, - * { - * "name":"f_measure", - * "flow_id":"12", - * "value":"0.922723", - * "array_data":"[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]" - * }, - * { - * "name":"kappa", - * "flow_id":"13", - * "value":"0.913601" - * } - * ] - * } - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n220 - Please provide run ID. In order to view run details, please provide the run ID.\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->run($segments[0]); return; } - /** - *@OA\Get( - * path="/run/reset/{id}", - * tags={"run"}, - * summary="Resets a run.", - * description="Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.", - * @OA\Parameter( - * name="id", - * in="path", - * type="string", - * description="Run ID.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="Id of the evaluated run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="run_reset", - * ref="#/components/schemas/inline_response_200_21_upload_flow", - * ), - * example={ - * "run_reset": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n412 - Run does not exist\n413 - Run is not owned by you\n394 - Resetting run failed\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && is_numeric($segments[1]) && $segments[0] == 'reset' && in_array($request_type, $getpost)) { $this->run_reset($segments[1]); return; } - /** - *@OA\Delete( - * path="/run/{id}", - * tags={"run"}, - * summary="Delete run", - * description="Deletes a run. Upon success, it returns the ID of the deleted run.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="data_delete", - * ref="#/components/schemas/inline_response_200_17_data_delete", - * ), - * example={ - * "run_delete": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n390 - Please provide API key. In order to remove your content, please authenticate.\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\n392 - Run does not exists. The run ID could not be linked to an existing run.\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->run_delete($segments[0]); return; } - /** - *@OA\Post( - * path="/run", - * tags={"run"}, - * summary="Upload run", - * description="Uploads a run. Upon success, it returns the run id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", - * required="true", - * ), - * @OA\Parameter( - * name="predictions", - * in="formData", - * type="file", - * description="The predictions generated by the run", - * required="true", - * ), - * @OA\Parameter( - * name="model_readable", - * in="formData", - * type="file", - * description="The human-readable model generated by the run", - * required="false", - * ), - * @OA\Parameter( - * name="model_serialized", - * in="formData", - * type="file", - * description="The serialized model generated by the run", - * required="false", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_18_upload_flow", - * ), - * example={ - * "upload_run": { - * "id": "2520" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n202 - Please provide run XML.\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\n204 - Unknown task. The task with the given ID was not found in the database.\n205 - Unknown flow. The flow with the given ID was not found in the database.\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\n207 - File upload failed. One of the files uploaded has a problem.\n208 - Error inserting setup record. Please contact api administrators\n210 - Unable to store run. Please contact api administrators.\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\n212 - Unable to store file. Please contact api administrators.\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\n214 - Unable to store input setting. Please contact API support team.\n215 - Unable to evaluate predictions. Please contact API support team.\n216 - Error thrown by Java Application. Additional information field is provided.\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\n219 - Error reading the XML document. The XML description file could not be verified.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 0 && $request_type == 'post') { $this->run_upload(); return; } - /** - *@OA\Post( - * path="/run/tag", - * tags={"run"}, - * summary="Tag a run", - * description="Tags a run.", - * @OA\Parameter( - * name="run_id", - * in="formData", - * type="number", - * format="integer", - * description="Id of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the tagged run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="run_tag", - * ref="#/components/schemas/inline_response_200_19_run_tag", - * ), - * example={ - * "run_tag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { - $this->entity_tag_untag('run', $this->input->post('run_id'), $this->input->post('tag'), false, 'run'); - return; - } - - /** - *@OA\Post( - * path="/run/untag", - * tags={"run"}, - * summary="Untag a run", - * description="Untags a run.", - * @OA\Parameter( - * name="run_id", - * in="formData", - * type="number", - * description="Id of the run.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the untagged run", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="run_untag", - * ref="#/components/schemas/inline_response_200_20_run_untag", - * ), - * example={ - * "run_untag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ + $this->run_tag($this->input->post('run_id'), $this->input->post('tag')); + return; + } + if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { - $this->entity_tag_untag('run', $this->input->post('run_id'), $this->input->post('tag'), true, 'run'); + $this->run_untag($this->input->post('run_id'), $this->input->post('tag')); return; } @@ -643,6 +103,123 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } + /** + *@OA\Post( + * path="/run/tag", + * tags={"run"}, + * summary="Tag a run", + * description="Tags a run.", + * @OA\Parameter( + * name="run_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the run.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_tag", + * ref="#/components/schemas/inline_response_200_19_run_tag", + * ), + * example={ + * "run_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function run_tag($run_id, $tag) { + $this->run_tag_untag($run_id, $tag, false); + } + + /** + *@OA\Post( + * path="/run/untag", + * tags={"run"}, + * summary="Untag a run", + * description="Untags a run.", + * @OA\Parameter( + * name="run_id", + * in="formData", + * type="number", + * description="Id of the run.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the untagged run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_untag", + * ref="#/components/schemas/inline_response_200_20_run_untag", + * ), + * example={ + * "run_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function run_untag($run_id, $tag) { + $this->run_tag_untag($run_id, $tag, true); + } + + private function run_tag_untag($run_id, $tag, $do_untag) { + $this->entity_tag_untag('run',$run_id, $tag, $do_untag, 'run'); + } + /** *@OA\Get( * path="/run/list/{filters}", @@ -662,14 +239,14 @@ function bootstrap($format, $segments, $request_type, $user_id) { /uploader/{ids} - return only runs uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. ", - * required="true", + * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", * type="string", * description="API key to authenticate the user", - * required="false", + * required=false, * ), * @OA\Response( * response=200, @@ -789,6 +366,124 @@ private function run_list($segs, $user_id) { $this->xmlContents('runs', $this->version, array('runs' => $res)); } + /** + *@OA\Get( + * path="/run/{id}", + * tags={"run"}, + * summary="Get run description", + * description="Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A run description", + * @OA\JsonContent( + * ref="#/components/schemas/Run", + * example={ + * "run": { + * "run_id":"100", + * "uploader":"1", + * "uploader_name":"Jan van Rijn", + * "task_id":"28", + * "task_type":"Supervised Classification", + * "task_evaluation_measure":"predictive_accuracy", + * "flow_id":"67", + * "flow_name":"weka.BayesNet_K2(1)", + * "setup_string":"weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", + * "parameter_setting": [ + * { + * "name":"D", + * "value":"true" + * }, + * { + * "name":"Q", + * "value":"weka.classifiers.bayes.net.search.local.K2" + * }, + * { + * "name":"P", + * "value":"1" + * }, + * { + * "name":"S", + * "value":"BAYES" + * } + * ], + * "input_data": + * { + * "dataset": + * { + * "did":"28", + * "name":"optdigits", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/28\\/dataset_28_optdigits.arff" + * } + * }, + * "output_data": + * { + * "file": [ + * { + * "did":"48838", + * "file_id":"261", + * "name":"description", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/261\\/weka_generated_run935374685998857626.xml" + * }, + * { + * "did":"48839", + * "file_id":"262", + * "name":"predictions", + * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff" + * } + * ], + * "evaluation": [ + * { + * "name":"area_under_roc_curve", + * "flow_id":"4", + * "value":"0.990288", + * "array_data":"[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]" + * }, + * { + * "name":"confusion_matrix", + * "flow_id":"10", + * "array_data":"[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]" + * }, + * { + * "name":"f_measure", + * "flow_id":"12", + * "value":"0.922723", + * "array_data":"[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]" + * }, + * { + * "name":"kappa", + * "flow_id":"13", + * "value":"0.913601" + * } + * ] + * } + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n220 - Please provide run ID. In order to view run details, please provide the run ID.\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run($run_id) { if( $run_id == false ) { $this->returnError( 235, $this->version ); @@ -815,6 +510,52 @@ private function run($run_id) { $this->xmlContents( 'run-get', $this->version, array( 'source' => $run ) ); } + /** + *@OA\Delete( + * path="/run/{id}", + * tags={"run"}, + * summary="Delete run", + * description="Deletes a run. Upon success, it returns the ID of the deleted run.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the run.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="data_delete", + * ref="#/components/schemas/inline_response_200_17_data_delete", + * ), + * example={ + * "run_delete": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n390 - Please provide API key. In order to remove your content, please authenticate.\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\n392 - Run does not exists. The run ID could not be linked to an existing run.\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_delete($run_id) { $run = $this->Run->getById( $run_id ); @@ -860,6 +601,51 @@ private function run_delete($run_id) { $this->xmlContents( 'run-delete', $this->version, array( 'run' => $run ) ); } + /** + *@OA\Get( + * path="/run/reset/{id}", + * tags={"run"}, + * summary="Resets a run.", + * description="Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.", + * @OA\Parameter( + * name="id", + * in="path", + * type="string", + * description="Run ID.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="Id of the evaluated run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="run_reset", + * ref="#/components/schemas/inline_response_200_21_upload_flow", + * ), + * example={ + * "run_reset": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n412 - Run does not exist\n413 - Run is not owned by you\n394 - Resetting run failed\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_reset($run_id) { $run = $this->Run->getById( $run_id ); @@ -882,6 +668,72 @@ private function run_reset($run_id) { $this->xmlContents( 'run-reset', $this->version, array( 'run' => $run ) ); } + /** + *@OA\Post( + * path="/run", + * tags={"run"}, + * summary="Upload run", + * description="Uploads a run. Upon success, it returns the run id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", + * required=true, + * ), + * @OA\Parameter( + * name="predictions", + * in="formData", + * type="file", + * description="The predictions generated by the run", + * required=true, + * ), + * @OA\Parameter( + * name="model_readable", + * in="formData", + * type="file", + * description="The human-readable model generated by the run", + * required=false, + * ), + * @OA\Parameter( + * name="model_serialized", + * in="formData", + * type="file", + * description="The serialized model generated by the run", + * required=false, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_18_upload_flow", + * ), + * example={ + * "upload_run": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n202 - Please provide run XML.\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\n204 - Unknown task. The task with the given ID was not found in the database.\n205 - Unknown flow. The flow with the given ID was not found in the database.\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\n207 - File upload failed. One of the files uploaded has a problem.\n208 - Error inserting setup record. Please contact api administrators\n210 - Unable to store run. Please contact api administrators.\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\n212 - Unable to store file. Please contact api administrators.\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\n214 - Unable to store input setting. Please contact API support team.\n215 - Unable to evaluate predictions. Please contact API support team.\n216 - Error thrown by Java Application. Additional information field is provided.\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\n219 - Error reading the XML document. The XML description file could not be verified.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_upload() { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -1184,6 +1036,68 @@ private function run_upload() { $this->xmlContents( 'run-upload', $this->version, $result ); } + /** + *@OA\Get( + * path="/run/trace/{id}", + * tags={"run"}, + * summary="Get run trace", + * description="Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A run trace description", + * @OA\JsonContent( + * ref="#/components/schemas/RunTrace", + * example={ + * "trace": { + * "run_id":"573055", + * "trace_iteration": { + * "repeat":"0", + * "fold":"0", + * "repeat":"0", + * "iteration":"0", + * "setup_string":{"parameter_minNumObj": "1", + * "parameter_confidenceFactor": "0.1"}, + * "evaluation":"94.814815", + * "selected": "true" + * }, + * "trace_iteration": { + * "repeat":"0", + * "fold":"0", + * "repeat":"0", + * "iteration":"1", + * "setup_string":{"parameter_minNumObj": "1", + * "parameter_confidenceFactor": "0.25"}, + * "evaluation": "94.074074", + * "selected": "true" + * } + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n570 - No successful trace associated with this run\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_trace($run_id) { $run = $this->Run->getById($run_id); if ($run === false) { @@ -1200,6 +1114,59 @@ private function run_trace($run_id) { $this->xmlContents('run-trace-get', $this->version, array('run_id' => $run_id, 'trace' => $trace)); } + /** + *@OA\Post( + * path="/run/trace/{id}", + * tags={"run"}, + * summary="Upload run trace", + * description="Uploads a run trace. Upon success, it returns the run id.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the run.", + * required=true, + * ), + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the run with the trace", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_23_upload_flow", + * ), + * example={ + * "run_trace": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n561 - Problem with uploaded trace file.\n562 - Problem validating xml trace file.\n563 - Problem loading xml trace file.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_trace_upload() { if (!$this->user_has_admin_rights) { $this->returnError(106, $this->version); @@ -1250,6 +1217,51 @@ private function run_trace_upload() { $this->xmlContents('run-trace', $this->version, array('run_id' => $run_id)); } + /** + *@OA\Post( + * path="/run/evaluate", + * tags={"run"}, + * summary="Uploads a run evaluation", + * description="Uploads a run evaluation. When successful, it returns the run id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the run evaluation.Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.evaluate) and an [XML example](https://www.openml.org/api/v1/xml_example/run.evaluate).", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the evaluated run", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_flow", + * ref="#/components/schemas/inline_response_200_21_upload_flow", + * ), + * example={ + * "run_evaluate": { + * "id": "2520" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n422 - Upload problem description XML\n423 - Problem validating uploaded description file\n424 - Problem opening description xml\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function run_evaluate() { $timestamps = array(microtime(true)); // profiling 0 if (!$this->user_has_admin_rights) { diff --git a/openml_OS/models/api/v1/Api_setup.php b/openml_OS/models/api/v1/Api_setup.php index 4bc0a8674..c056236c8 100644 --- a/openml_OS/models/api/v1/Api_setup.php +++ b/openml_OS/models/api/v1/Api_setup.php @@ -24,321 +24,29 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); - /** - *@OA\Get( - * path="/setup/{id}", - * tags={"setup"}, - * summary="Get a hyperparameter setup", - * description="Returns information about a setup. The information includes the list of hyperparameters, with name, value, and default value.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A setup description", - * @OA\JsonContent( - * ref="#/components/schemas/Setup", - * example={ - * "setup_parameters":{ - * "flow_id":"59", - * "parameter":[ - * { - * "full_name":"weka.JRip(1)_F", - * "parameter_name":"F", - * "data_type":"option", - * "default_value":"3", - * "value":"3" - * },{ - * "full_name":"weka.JRip(1)_N", - * "parameter_name":"N", - * "data_type":"option", - * "default_value":"2.0", - * "value":"2.0" - * },{ - * "full_name":"weka.JRip(1)_O", - * "parameter_name":"O", - * "data_type":"option", - * "default_value":"2", - * "value":"2" - * },{ - * "full_name":"weka.JRip(1)_S", - * "parameter_name":"S", - * "data_type":"option", - * "default_value":"1", - * "value":"1" - * }] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n280 - Please provide setup ID. In order to view setup details, please provide the run ID\n281 - Setup not found. The setup ID was invalid, or setup does not exist (anymore).\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->setup($segments[0]); return; } - /** - *@OA\Get( - * path="/setup/list/{filters}", - * tags={"setup"}, - * summary="List and filter setups", - * description="List setups, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/setup/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 1,000 results are returned at a time, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain flows, setups, or tags.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /tag/{tag} - returns only setups tagged with the given tag. - /flow/{ids} - return only setups for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' - /setup/{ids} - return only specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of setup descriptions", - * @OA\JsonContent( - * ref="#/components/schemas/SetupList", - * example={ - * "setups": { - * "setup": [ - * { - * "setup_id":"10", - * "flow_id":"65", - * "parameter": [ - * { - * "id":"4144", - * "flow_id":"65", - * "flow_name":"weka.RandomForest", - * "full_name":"weka.RandomForest(1)_I", - * "parameter_name":"I", - * "data_type":"option", - * "default_value":"10", - * "value":"10" - * }, - * { - * "id":"4145", - * "flow_id":"65", - * "flow_name":"weka.RandomForest", - * "full_name":"weka.RandomForest(1)_K", - * "parameter_name":"K", - * "data_type":"option", - * "default_value":"0", - * "value":"0" - * } - * ] - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n670 - Please specify at least one filter.\n671 - Illegal filter.\n672 - Illegal filter input.\n673 - Result set too big. Please use one of the filters or the limit option.\n674 - No results, please check the filter.\n675 - Cannot specify offset without limit.\n676 - Requested result limit too high.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->setup_list($segments); return; } - /** - *@OA\Delete( - * path="/setup/{id}", - * tags={"setup"}, - * summary="Delete setup", - * description="Deletes a setup. Upon success, it returns the ID of the deleted setup.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the setup.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted setup", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="study_delete", - * ref="#/components/schemas/inline_response_200_14_study_delete", - * ), - * example={ - * "setup_delete": { - * "id": "1" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n401 - Authentication failed. Please provide API key. In order to remove your content, please authenticate.\n402 - Setup does not exists. The setup ID could not be linked to an existing setup.\n404 - Setup deletion failed. Setup is in use by other content (runs, schedules, etc). Can not be deleted.\n405 - Setup deletion failed. Please try again later.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->setup_delete($segments[0]); return; } - /** - *@OA\Post( - * path="/setup/tag", - * tags={"setup"}, - * summary="Tag a setup", - * description="Tags a setup.", - * @OA\Parameter( - * name="setup_id", - * in="formData", - * type="number", - * format="integer", - * description="Id of the setup.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the tagged setup", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_tag", - * ref="#/components/schemas/inline_response_200_15_flow_tag", - * ), - * example={ - * "setup_tag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { - $this->entity_tag_untag('algorithm_setup', $this->input->post('setup_id'), $this->input->post('tag'), false, 'setup'); + $this->setup_tag( $this->input->post('setup_id'), $this->input->post('tag')); return; } - /** - *@OA\Post( - * path="/setup/untag", - * tags={"setup"}, - * summary="Untag a setup", - * description="Untags a setup.", - * @OA\Parameter( - * name="setup_id", - * in="formData", - * type="number", - * description="Id of the setup.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the untagged setup", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="flow_untag", - * ref="#/components/schemas/inline_response_200_16_flow_untag", - * ), - * example={ - * "setup_untag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { - $this->entity_tag_untag('algorithm_setup', $this->input->post('setup_id'), $this->input->post('tag'), true, 'setup'); + $this->setup_untag($this->input->post('setup_id'), $this->input->post('tag')); return; } @@ -376,6 +84,191 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError( 100, $this->version ); } + /** + *@OA\Post( + * path="/setup/tag", + * tags={"setup"}, + * summary="Tag a setup", + * description="Tags a setup.", + * @OA\Parameter( + * name="setup_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the setup.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged setup", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_tag", + * ref="#/components/schemas/inline_response_200_15_flow_tag", + * ), + * example={ + * "setup_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function setup_tag($setup_id, $tag) { + $this->setup_tag_untag($setup_id, $tag, false); + } + + /** + *@OA\Post( + * path="/setup/untag", + * tags={"setup"}, + * summary="Untag a setup", + * description="Untags a setup.", + * @OA\Parameter( + * name="setup_id", + * in="formData", + * type="number", + * description="Id of the setup.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the untagged setup", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="flow_untag", + * ref="#/components/schemas/inline_response_200_16_flow_untag", + * ), + * example={ + * "setup_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function setup_untag($setup_id, $tag) { + $this->setup_tag_untag($setup_id, $tag, true); + } + + private function setup_tag_untag($setup_id, $tag, $do_untag) { + $this->entity_tag_untag('algorithm_setup', $setup_id, $tag, $do_untag, 'setup'); + } + + /** + *@OA\Get( + * path="/setup/{id}", + * tags={"setup"}, + * summary="Get a hyperparameter setup", + * description="Returns information about a setup. The information includes the list of hyperparameters, with name, value, and default value.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A setup description", + * @OA\JsonContent( + * ref="#/components/schemas/Setup", + * example={ + * "setup_parameters":{ + * "flow_id":"59", + * "parameter":[ + * { + * "full_name":"weka.JRip(1)_F", + * "parameter_name":"F", + * "data_type":"option", + * "default_value":"3", + * "value":"3" + * },{ + * "full_name":"weka.JRip(1)_N", + * "parameter_name":"N", + * "data_type":"option", + * "default_value":"2.0", + * "value":"2.0" + * },{ + * "full_name":"weka.JRip(1)_O", + * "parameter_name":"O", + * "data_type":"option", + * "default_value":"2", + * "value":"2" + * },{ + * "full_name":"weka.JRip(1)_S", + * "parameter_name":"S", + * "data_type":"option", + * "default_value":"1", + * "value":"1" + * }] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n280 - Please provide setup ID. In order to view setup details, please provide the run ID\n281 - Setup not found. The setup ID was invalid, or setup does not exist (anymore).\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function setup($setup_id) { if($setup_id == false) { $this->returnError(280, $this->version); @@ -397,7 +290,80 @@ private function setup($setup_id) { $this->xmlContents('setup-parameters', $this->version, array('parameters' => $this->parameters, 'setup' => $setup)); } } - + + /** + *@OA\Get( + * path="/setup/list/{filters}", + * tags={"setup"}, + * summary="List and filter setups", + * description="List setups, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/setup/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 1,000 results are returned at a time, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain flows, setups, or tags.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /tag/{tag} - returns only setups tagged with the given tag. + /flow/{ids} - return only setups for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' + /setup/{ids} - return only specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + ", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of setup descriptions", + * @OA\JsonContent( + * ref="#/components/schemas/SetupList", + * example={ + * "setups": { + * "setup": [ + * { + * "setup_id":"10", + * "flow_id":"65", + * "parameter": [ + * { + * "id":"4144", + * "flow_id":"65", + * "flow_name":"weka.RandomForest", + * "full_name":"weka.RandomForest(1)_I", + * "parameter_name":"I", + * "data_type":"option", + * "default_value":"10", + * "value":"10" + * }, + * { + * "id":"4145", + * "flow_id":"65", + * "flow_name":"weka.RandomForest", + * "full_name":"weka.RandomForest(1)_K", + * "parameter_name":"K", + * "data_type":"option", + * "default_value":"0", + * "value":"0" + * } + * ] + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n670 - Please specify at least one filter.\n671 - Illegal filter.\n672 - Illegal filter input.\n673 - Result set too big. Please use one of the filters or the limit option.\n674 - No results, please check the filter.\n675 - Cannot specify offset without limit.\n676 - Requested result limit too high.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ function setup_list($segs) { $result_limit = 1000; $legal_filters = array('flow', 'setup', 'limit', 'offset', 'tag'); @@ -572,6 +538,52 @@ private function setup_exists($partial) { } } + /** + *@OA\Delete( + * path="/setup/{id}", + * tags={"setup"}, + * summary="Delete setup", + * description="Deletes a setup. Upon success, it returns the ID of the deleted setup.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the setup.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted setup", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_delete", + * ref="#/components/schemas/inline_response_200_14_study_delete", + * ), + * example={ + * "setup_delete": { + * "id": "1" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n401 - Authentication failed. Please provide API key. In order to remove your content, please authenticate.\n402 - Setup does not exists. The setup ID could not be linked to an existing setup.\n404 - Setup deletion failed. Setup is in use by other content (runs, schedules, etc). Can not be deleted.\n405 - Setup deletion failed. Please try again later.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function setup_delete($setup_id) { $setup = $this->Algorithm_setup->getById( $setup_id ); diff --git a/openml_OS/models/api/v1/Api_study.php b/openml_OS/models/api/v1/Api_study.php index 3ef951359..94d921cf9 100644 --- a/openml_OS/models/api/v1/Api_study.php +++ b/openml_OS/models/api/v1/Api_study.php @@ -16,117 +16,12 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); - /** - *@OA\Get( - * path="/study/list/{filters}", - * tags={"study"}, - * summary="List all studies (collections of items)", - * description="List studies, optionally filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/study/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all studies that match the constraints.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /main_entity_type/{type} - only return studies collecting entities of a given type (e.g. 'task' or 'run'). - /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of studies", - * @OA\JsonContent( - * ref="#/components/schemas/StudyList", - * example={ - * "study_list":{ - * "study":[ - * { - * "id":"1", - * "alias":"Study_1", - * "name":"A large-scale comparison of classification algorithms", - * "creation_date":"2017-07-20 15:51:20", - * "creator":"2" - * }, - * { - * "id":"2", - * "alias":"Study_2", - * "name":"Fast Algorithm Selection using Learning Curves", - * "creation_date":"2017-07-20 15:51:20", - * "creator":"2" - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) > 0 && $segments[0] == 'list') { array_shift($segments); $this->study_list($segments); return; } - /** - *@OA\Post( - * path="/study", - * tags={"study"}, - * summary="Create new study", - * description="Creates a new study. Upon success, it returns the study id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the study. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.study.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/study).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded study", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_study", - * ref="#/components/schemas/inline_response_200_25_upload_study", - * ), - * example={ - * "upload_study": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n1031 - Description file not present. Please upload the study description.\n1032 - Problem validating uploaded description file. The XML description format does not meet the standards. See the XSD schema.\n1033 - Illegal main entity type. Currently only collections of tasks and can be created.\n1034 - Linked entities are not of the correct type fot this study.\n1035 - Benchmark suites can only be linked to run studies.\n1036 - Referred benchmark suite cannot be found.\n1037 - Referred benchmark suite should be a task collection.\n1038 - Study alias is not unique.\n1039 - Dataset insertion problem. Please contact the administrators.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 0) { $this->study_create(); return; @@ -137,174 +32,18 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Delete( - * path="/study/{id}", - * tags={"study"}, - * summary="Delete study", - * description="Deletes a study. Upon success, it returns the ID of the deleted study.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the study.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted study", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="study_delete", - * ref="#/components/schemas/inline_response_200_24_study_delete", - * ), - * example={ - * "study_delete": { - * "id": "1" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n591 - Please provide API key. In order to remove your content, please authenticate.\n592 - Study does not exists. The study ID could not be linked to an existing study.\n593 - Study deletion failed. Please try again later.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->study_delete($segments[0]); return; } - /** - *@OA\Post( - * path="/study/{id}/attach", - * tags={"study"}, - * summary="Attach a new entity to a study", - * description="Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the study. Supplied in the URL path.", - * required="true", - * ), - * @OA\Parameter( - * name="ids", - * in="formData", - * type="string", - * description="Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Properties of the updated study", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="study_attach", - * ref="#/components/schemas/inline_response_200_26_study_attach", - * ), - * example={ - * "study_attach": { - * "id": "1", - * "main_entity_type": "task", - * "linked_entities": "5" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannnot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 2 && is_numeric($segments[0]) && $segments[1] == 'attach' && $request_type == 'post') { - $this->study_attach_detach($segments[0], true); - return; - } - - /** - *@OA\Post( - * path="/study/{id}/detach", - * tags={"study"}, - * summary="Detach an entity from a study", - * description="Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the study.", - * required="true", - * ), - * @OA\Parameter( - * name="ids", - * in="formData", - * type="string", - * description="Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Properties of the updated study", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_study", - * ref="#/components/schemas/inline_response_200_26_study_attach", - * ), - * example={ - * "study_detach": { - * "id": "1", - * "main_entity_type": "task", - * "linked_entities": "5" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ + $this->study_attach($segments[0]); + return; + } + if (count($segments) == 2 && is_numeric($segments[0]) && $segments[1] == 'detach' && $request_type == 'post') { - $this->study_attach_detach($segments[0], false); + $this->study_detach($segments[0]); return; } @@ -313,67 +52,58 @@ function bootstrap($format, $segments, $request_type, $user_id) { if (count($segments) == 2) { $type = $segments[1]; } - - /** - *@OA\Get( - * path="/study/{id}", - * tags={"study"}, - * summary="Get study description by study id or alias", - * description="Returns information about the study with the given id or alias.", - * @OA\Parameter( - * name="id", - * in="path", - * type="string", - * description="ID or alias of the study.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A study description", - * @OA\JsonContent( - * ref="#/components/schemas/Study", - * example={ - * "study": { - * "id": "99", - * "main_entity_type": "task", - * "name": "CC18 benchmark suite", - * "description": "CC18 benchmark suite", - * "creation_date": "2019-02-16T17:35:58", - * "creator": "1159", - * "data": {"data_id": ["1","2","3"]}, - * "tasks": {"task_id": ["1","2","3"]} - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n601 - Unknown study. The study with the given id or alias was not found in the database\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ - if (is_numeric($segments[0])) { - $this->study_by_id($segments[0], $type); - return; - } else { - $this->study_by_alias($segments[0], $type); - return; - } + $this->study_get($segments[0], $type); + return; } $this->returnError(100, $this->version); } - + + /** + *@OA\Post( + * path="/study", + * tags={"study"}, + * summary="Create new study", + * description="Creates a new study. Upon success, it returns the study id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the study. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.study.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/study).", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_study", + * ref="#/components/schemas/inline_response_200_25_upload_study", + * ), + * example={ + * "upload_study": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1031 - Description file not present. Please upload the study description.\n1032 - Problem validating uploaded description file. The XML description format does not meet the standards. See the XSD schema.\n1033 - Illegal main entity type. Currently only collections of tasks and can be created.\n1034 - Linked entities are not of the correct type fot this study.\n1035 - Benchmark suites can only be linked to run studies.\n1036 - Referred benchmark suite cannot be found.\n1037 - Referred benchmark suite should be a task collection.\n1038 - Study alias is not unique.\n1039 - Dataset insertion problem. Please contact the administrators.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function study_create() { $xsdFile = xsd('openml.study.upload', $this->controller, $this->version); @@ -516,6 +246,124 @@ private function status_update($study_id, $status) { $this->xmlContents('study-status-update', $this->version, $template_vars); } + /** + *@OA\Post( + * path="/study/{id}/attach", + * tags={"study"}, + * summary="Attach a new entity to a study", + * description="Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study. Supplied in the URL path.", + * required=true, + * ), + * @OA\Parameter( + * name="ids", + * in="formData", + * type="string", + * description="Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Properties of the updated study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_attach", + * ref="#/components/schemas/inline_response_200_26_study_attach", + * ), + * example={ + * "study_attach": { + * "id": "1", + * "main_entity_type": "task", + * "linked_entities": "5" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannnot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function study_attach($study_id) { + $this->study_attach_detach($study_id, true); + } + + /** + *@OA\Post( + * path="/study/{id}/detach", + * tags={"study"}, + * summary="Detach an entity from a study", + * description="Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study.", + * required=true, + * ), + * @OA\Parameter( + * name="ids", + * in="formData", + * type="string", + * description="Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Properties of the updated study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_study", + * ref="#/components/schemas/inline_response_200_26_study_attach", + * ), + * example={ + * "study_detach": { + * "id": "1", + * "main_entity_type": "task", + * "linked_entities": "5" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannot attach entities to legacy studies.\n1043 - Please provide POST field 'ids'.\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function study_detach($study_id) { + $this->study_attach_detach($study_id, false); + } + private function study_attach_detach($study_id, $attach) { $study = $this->Study->getById($study_id); if ($study === false) { @@ -584,7 +432,53 @@ private function study_attach_detach($study_id, $attach) { $this->xmlContents('study-attach-detach', $this->version, $template_vars); } - + + /** + *@OA\Delete( + * path="/study/{id}", + * tags={"study"}, + * summary="Delete study", + * description="Deletes a study. Upon success, it returns the ID of the deleted study.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the study.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted study", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="study_delete", + * ref="#/components/schemas/inline_response_200_24_study_delete", + * ), + * example={ + * "study_delete": { + * "id": "1" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n591 - Please provide API key. In order to remove your content, please authenticate.\n592 - Study does not exists. The study ID could not be linked to an existing study.\n593 - Study deletion failed. Please try again later.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function study_delete($study_id) { $study = $this->Study->getById($study_id); @@ -622,6 +516,66 @@ private function study_delete($study_id) { $this->xmlContents('study-delete', $this->version, array('study' => $study)); } + /** + *@OA\Get( + * path="/study/list/{filters}", + * tags={"study"}, + * summary="List all studies (collections of items)", + * description="List studies, optionally filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/study/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all studies that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /main_entity_type/{type} - only return studies collecting entities of a given type (e.g. 'task' or 'run'). + /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. + ", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of studies", + * @OA\JsonContent( + * ref="#/components/schemas/StudyList", + * example={ + * "study_list":{ + * "study":[ + * { + * "id":"1", + * "alias":"Study_1", + * "name":"A large-scale comparison of classification algorithms", + * "creation_date":"2017-07-20 15:51:20", + * "creator":"2" + * }, + * { + * "id":"2", + * "alias":"Study_2", + * "name":"Fast Algorithm Selection using Learning Curves", + * "creation_date":"2017-07-20 15:51:20", + * "creator":"2" + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function study_list($segs) { $legal_filters = array('limit', 'offset', 'main_entity_type', 'uploader', 'status', 'benchmark_suite'); @@ -676,6 +630,64 @@ private function study_list($segs) { $this->xmlContents('study-list', $this->version, array('studies' => $studies)); } + /** + *@OA\Get( + * path="/study/{id}", + * tags={"study"}, + * summary="Get study description by study id or alias", + * description="Returns information about the study with the given id or alias.", + * @OA\Parameter( + * name="id", + * in="path", + * type="string", + * description="ID or alias of the study.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A study description", + * @OA\JsonContent( + * ref="#/components/schemas/Study", + * example={ + * "study": { + * "id": "99", + * "main_entity_type": "task", + * "name": "CC18 benchmark suite", + * "description": "CC18 benchmark suite", + * "creation_date": "2019-02-16T17:35:58", + * "creator": "1159", + * "data": {"data_id": ["1","2","3"]}, + * "tasks": {"task_id": ["1","2","3"]} + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n601 - Unknown study. The study with the given id or alias was not found in the database\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function study_get($study_id, $type) { + if (is_numeric($study_id)) { + $this->study_by_id($study_id, $type); + return; + } else { + $this->study_by_alias($study_id, $type); + return; + } + } + private function study_by_id($study_id, $entity_type) { $study = $this->Study->getById($study_id); diff --git a/openml_OS/models/api/v1/Api_task.php b/openml_OS/models/api/v1/Api_task.php index a22e317c2..c1aaa2be3 100644 --- a/openml_OS/models/api/v1/Api_task.php +++ b/openml_OS/models/api/v1/Api_task.php @@ -27,256 +27,12 @@ function bootstrap($format, $segments, $request_type, $user_id) { $getpost = array('get','post'); - /** - *@OA\Get( - * path="/task/list/{filters}", - * tags={"task"}, - * summary="List and filter tasks", - * description="List tasks, possibly filtered by a range of properties from the task itself or from the underlying dataset. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all tasks that match the constraints.", - * @OA\Parameter( - * name="filters", - * in="path", - * type="string", - * description="Any combination of these filters - /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. - /status/{status} - returns only tasks with a given status, either 'active', 'deactivated', or 'in_preparation'. - /type/{type_id} - returns only tasks with a given task type id. See the list of task types of the ID's (e.g. 1 = Supervised Classification). - /tag/{tag} - returns only tasks tagged with the given tag. - /data_tag/{tag} - returns only tasks for which the underlying dataset is tagged with the given tag. - /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'. - ", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of tasks with the given tag", - * @OA\JsonContent( - * ref="#/components/schemas/TaskList", - * example={ - * "task": { - * "task": [ - * { - * "task_id":"1", - * "task_type":"Supervised Classification", - * "did":"1", - * "name":"anneal", - * "status":"active", - * "format":"ARFF", - * "input":[ - * { - * "name":"estimation_procedure", - * "value":"1" - * }, - * { - * "name":"evaluation_measures", - * "value":"predictive_accuracy" - * }, - * { - * "name":"source_data", - * "value":"1" - * }, - * { - * "name":"target_feature", - * "value":"class" - * } - * ], - * "quality":[ - * { - * "name":"MajorityClassSize", - * "value":"684" - * }, - * { - * "name":"MaxNominalAttDistinctValues", - * "value":"10.0" - * }, - * { - * "name":"MinorityClassSize", - * "value":"0" - * }, - * { - * "name":"NumBinaryAtts", - * "value":"14.0" - * }, - * { - * "name":"NumberOfClasses", - * "value":"6" - * }, - * { - * "name":"NumberOfFeatures", - * "value":"39" - * }, - * { - * "name":"NumberOfInstances", - * "value":"898" - * }, - * { - * "name":"NumberOfInstancesWithMissingValues", - * "value":"0" - * }, - * { - * "name":"NumberOfMissingValues", - * "value":"0" - * }, - * { - * "name":"NumberOfNumericFeatures", - * "value":"6" - * }, - * { - * "name":"NumberOfSymbolicFeatures", - * "value":"32" - * } - * ], - * "tag":[ - * "basic", - * "study_1", - * "study_7", - * "under100k", - * "under1m" - * ] - * } - * ] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n480 - Illegal filter specified.\n481 - Filter values/ranges not properly specified.\n482 - No results. There where no matches for the given constraints.\n483 - Can not specify an offset without a limit.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->task_list($segments, $user_id); return; } - /** - *@OA\Get( - * path="/task/{id}", - * tags={"task"}, - * summary="Get task description", - * description="Returns information about a task. The information includes the task type, input data, train/test sets, and more.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="ID of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A task description", - * @OA\JsonContent( - * ref="#/components/schemas/Task", - * example={ - * "task": { - * "task_id":"1", - * "task_type":"Supervised Classification", - * "input":[ - * { - * "name":"source_data", - * "data_set":{ - * "data_set_id":"1", - * "target_feature":"class" - * } - * }, - * { - * "name":"estimation_procedure", - * "estimation_procedure":{ - * "type":"crossvalidation", - * "data_splits_url":"https://www.openml.org/api_splits/get/1/Task_1_splits.arff", - * "parameter":[ - * { - * "name":"number_repeats", - * "value":"1" - * }, - * { - * "name":"number_folds", - * "value":"10" - * }, - * { - * "name":"percentage" - * }, - * { - * "name":"stratified_sampling", - * "value":"true" - * } - * ] - * } - * }, - * { - * "name":"cost_matrix", - * "cost_matrix":[] - * }, - * { - * "name":"evaluation_measures", - * "evaluation_measures": - * { - * "evaluation_measure":"predictive_accuracy" - * } - * } - * ], - * "output":{ - * "name":"predictions", - * "predictions":{ - * "format":"ARFF", - * "feature":[ - * { - * "name":"repeat", - * "type":"integer" - * }, - * { - * "name":"fold", - * "type":"integer" - * }, - * { - * "name":"row_id", - * "type":"integer" - * }, - * { - * "name":"confidence.classname", - * "type":"numeric" - * }, - * { - * "name":"prediction", - * "type":"string" - * } - * ] - * } - * }, - * "tag":["basic","study_1","under100k","under1m"] - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n150 - Please provide task_id.\n151 - Unknown task. The task with the given id was not found in the database\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && in_array($request_type, $getpost)) { $this->task($segments[0]); return; @@ -287,219 +43,23 @@ function bootstrap($format, $segments, $request_type, $user_id) { return; } - /** - *@OA\Post( - * path="/task", - * tags={"task"}, - * summary="Upload task", - * description="Uploads a task. Upon success, it returns the task id.", - * @OA\Parameter( - * name="description", - * in="formData", - * type="file", - * description="An XML file describing the task. Only name, description, and task format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.task.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/task).", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="Id of the uploaded task", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="upload_task", - * ref="#/components/schemas/inline_response_200_5_upload_task", - * ), - * example={ - * "upload_task": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n530 - Description file not present. Please upload the task description.\n531 - Internal error. Please contact api support team\n532 - Problem validating uploaded description file. The XML description format does not meet the standards\n533 - Task already exists.\n534 - Error creating the task.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 0 && $request_type == 'post') { $this->task_upload(); return; } - /** - *@OA\Delete( - * path="/task/{id}", - * tags={"task"}, - * summary="Delete task", - * description="Deletes a task. Upon success, it returns the ID of the deleted task.", - * @OA\Parameter( - * name="id", - * in="path", - * type="number", - * format="integer", - * description="Id of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="ID of the deleted task", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="task_delete", - * ref="#/components/schemas/inline_response_200_4_task_delete", - * ), - * example={ - * "task_delete": { - * "id": "4328" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n450 - Please provide API key. In order to remove your content, please authenticate.\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n452 - Task does not exists. The task ID could not be linked to an existing task.\n454 - Task is executed in some runs. Delete these first.\n455 - Deleting the task failed. Please contact support team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && is_numeric($segments[0]) && $request_type == 'delete') { $this->task_delete($segments[0]); return; } - /** - *@OA\Post( - * path="/task/tag", - * tags={"task"}, - * summary="Tag a task", - * description="Tags a task.", - * @OA\Parameter( - * name="task_id", - * in="formData", - * type="number", - * format="integer", - * description="Id of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="The id of the tagged task", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="task_tag", - * ref="#/components/schemas/inline_response_200_6_task_tag", - * ), - * example={ - * "task_tag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, task, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'tag' && $request_type == 'post') { - $this->entity_tag_untag('task', $this->input->post('task_id'), $this->input->post('tag'), false, 'task'); + $this->task_tag($this->input->post('task_id'), $this->input->post('tag')); return; } - /** - *@OA\Post( - * path="/task/untag", - * tags={"task"}, - * summary="Untag a task", - * description="Untags a task.", - * @OA\Parameter( - * name="task_id", - * in="formData", - * type="number", - * description="Id of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="tag", - * in="formData", - * type="string", - * description="Tag name", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="formData", - * type="string", - * description="Api key to authenticate the user", - * required="true", - * ), - * @OA\Response( - * response=200, - * description="A the features of the task", - * @OA\JsonContent( - * type="object", - * @OA\Property( - * property="task_untag", - * ref="#/components/schemas/inline_response_200_7_task_untag", - * ), - * example={ - * "task_untag": { - * "id": "2" - * } - * } - * ), - * ), - * @OA\Response( - * response=412, - * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, task, flow, run} not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, task, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", - * @OA\JsonContent( - * ref="#/components/schemas/Error", - * ), - * ), - *) - */ if (count($segments) == 1 && $segments[0] == 'untag' && $request_type == 'post') { - $this->entity_tag_untag('task', $this->input->post('task_id'), $this->input->post('tag'), true, 'task'); + $this->task_untag($this->input->post('task_id'), $this->input->post('tag')); return; } @@ -511,7 +71,251 @@ function bootstrap($format, $segments, $request_type, $user_id) { $this->returnError(100, $this->version); } + /** + *@OA\Post( + * path="/task/tag", + * tags={"task"}, + * summary="Tag a task", + * description="Tags a task.", + * @OA\Parameter( + * name="task_id", + * in="formData", + * type="number", + * format="integer", + * description="Id of the task.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="The id of the tagged task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_tag", + * ref="#/components/schemas/inline_response_200_6_task_tag", + * ), + * example={ + * "task_tag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, task, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function task_tag($task_id, $tag) { + $this->task_tag_untag($task_id, $tag, false); + } + + /** + *@OA\Post( + * path="/task/untag", + * tags={"task"}, + * summary="Untag a task", + * description="Untags a task.", + * @OA\Parameter( + * name="task_id", + * in="formData", + * type="number", + * description="Id of the task.", + * required=true, + * ), + * @OA\Parameter( + * name="tag", + * in="formData", + * type="string", + * description="Tag name", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="formData", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="A the features of the task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_untag", + * ref="#/components/schemas/inline_response_200_7_task_untag", + * ), + * example={ + * "task_untag": { + * "id": "2" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, task, flow, run} not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, task, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ + private function task_untag($task_id, $tag) { + $this->task_untag($task_id, $tag, true); + } + + private function task_tag_untag($task_id, $tag, $do_untag) { + $this->entity_tag_untag('task', $task_id, $tag, $do_untag, 'task'); + } + /** + *@OA\Get( + * path="/task/list/{filters}", + * tags={"task"}, + * summary="List and filter tasks", + * description="List tasks, possibly filtered by a range of properties from the task itself or from the underlying dataset. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all tasks that match the constraints.", + * @OA\Parameter( + * name="filters", + * in="path", + * type="string", + * description="Any combination of these filters + /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. + /status/{status} - returns only tasks with a given status, either 'active', 'deactivated', or 'in_preparation'. + /type/{type_id} - returns only tasks with a given task type id. See the list of task types of the ID's (e.g. 1 = Supervised Classification). + /tag/{tag} - returns only tasks tagged with the given tag. + /data_tag/{tag} - returns only tasks for which the underlying dataset is tagged with the given tag. + /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'. + ", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of tasks with the given tag", + * @OA\JsonContent( + * ref="#/components/schemas/TaskList", + * example={ + * "task": { + * "task": [ + * { + * "task_id":"1", + * "task_type":"Supervised Classification", + * "did":"1", + * "name":"anneal", + * "status":"active", + * "format":"ARFF", + * "input":[ + * { + * "name":"estimation_procedure", + * "value":"1" + * }, + * { + * "name":"evaluation_measures", + * "value":"predictive_accuracy" + * }, + * { + * "name":"source_data", + * "value":"1" + * }, + * { + * "name":"target_feature", + * "value":"class" + * } + * ], + * "quality":[ + * { + * "name":"MajorityClassSize", + * "value":"684" + * }, + * { + * "name":"MaxNominalAttDistinctValues", + * "value":"10.0" + * }, + * { + * "name":"MinorityClassSize", + * "value":"0" + * }, + * { + * "name":"NumBinaryAtts", + * "value":"14.0" + * }, + * { + * "name":"NumberOfClasses", + * "value":"6" + * }, + * { + * "name":"NumberOfFeatures", + * "value":"39" + * }, + * { + * "name":"NumberOfInstances", + * "value":"898" + * }, + * { + * "name":"NumberOfInstancesWithMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfMissingValues", + * "value":"0" + * }, + * { + * "name":"NumberOfNumericFeatures", + * "value":"6" + * }, + * { + * "name":"NumberOfSymbolicFeatures", + * "value":"32" + * } + * ], + * "tag":[ + * "basic", + * "study_1", + * "study_7", + * "under100k", + * "under1m" + * ] + * } + * ] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n480 - Illegal filter specified.\n481 - Filter values/ranges not properly specified.\n482 - No results. There where no matches for the given constraints.\n483 - Can not specify an offset without a limit.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function task_list($segs, $user_id) { $legal_filters = array('type', 'tag', 'data_tag', 'status', 'limit', 'offset', 'task_id', 'data_id', 'data_name', 'number_instances', 'number_features', 'number_classes', 'number_missing_values'); @@ -589,6 +393,122 @@ private function task_list($segs, $user_id) { $this->xmlContents( 'tasks', $this->version, array( 'tasks' => $tasks_res ) ); } + /** + *@OA\Get( + * path="/task/{id}", + * tags={"task"}, + * summary="Get task description", + * description="Returns information about a task. The information includes the task type, input data, train/test sets, and more.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="ID of the task.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A task description", + * @OA\JsonContent( + * ref="#/components/schemas/Task", + * example={ + * "task": { + * "task_id":"1", + * "task_type":"Supervised Classification", + * "input":[ + * { + * "name":"source_data", + * "data_set":{ + * "data_set_id":"1", + * "target_feature":"class" + * } + * }, + * { + * "name":"estimation_procedure", + * "estimation_procedure":{ + * "type":"crossvalidation", + * "data_splits_url":"https://www.openml.org/api_splits/get/1/Task_1_splits.arff", + * "parameter":[ + * { + * "name":"number_repeats", + * "value":"1" + * }, + * { + * "name":"number_folds", + * "value":"10" + * }, + * { + * "name":"percentage" + * }, + * { + * "name":"stratified_sampling", + * "value":"true" + * } + * ] + * } + * }, + * { + * "name":"cost_matrix", + * "cost_matrix":[] + * }, + * { + * "name":"evaluation_measures", + * "evaluation_measures": + * { + * "evaluation_measure":"predictive_accuracy" + * } + * } + * ], + * "output":{ + * "name":"predictions", + * "predictions":{ + * "format":"ARFF", + * "feature":[ + * { + * "name":"repeat", + * "type":"integer" + * }, + * { + * "name":"fold", + * "type":"integer" + * }, + * { + * "name":"row_id", + * "type":"integer" + * }, + * { + * "name":"confidence.classname", + * "type":"numeric" + * }, + * { + * "name":"prediction", + * "type":"string" + * } + * ] + * } + * }, + * "tag":["basic","study_1","under100k","under1m"] + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n150 - Please provide task_id.\n151 - Unknown task. The task with the given id was not found in the database\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function task($task_id) { $task = $this->Task->getById($task_id); if($task === false) { @@ -636,6 +556,52 @@ private function task_inputs($task_id) { $this->xmlContents('task-inputs', $this->version, array('task' => $task, 'inputs' => $inputs)); } + /** + *@OA\Delete( + * path="/task/{id}", + * tags={"task"}, + * summary="Delete task", + * description="Deletes a task. Upon success, it returns the ID of the deleted task.", + * @OA\Parameter( + * name="id", + * in="path", + * type="number", + * format="integer", + * description="Id of the task.", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="ID of the deleted task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="task_delete", + * ref="#/components/schemas/inline_response_200_4_task_delete", + * ), + * example={ + * "task_delete": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n450 - Please provide API key. In order to remove your content, please authenticate.\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n452 - Task does not exists. The task ID could not be linked to an existing task.\n454 - Task is executed in some runs. Delete these first.\n455 - Deleting the task failed. Please contact support team.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ private function task_delete($task_id) { $task = $this->Task->getById($task_id); @@ -682,6 +648,51 @@ private function task_delete($task_id) { $this->xmlContents( 'task-delete', $this->version, array( 'task' => $task ) ); } + /** + *@OA\Post( + * path="/task", + * tags={"task"}, + * summary="Upload task", + * description="Uploads a task. Upon success, it returns the task id.", + * @OA\Parameter( + * name="description", + * in="formData", + * type="file", + * description="An XML file describing the task. Only name, description, and task format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.task.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/task).", + * required=true, + * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="Api key to authenticate the user", + * required=true, + * ), + * @OA\Response( + * response=200, + * description="Id of the uploaded task", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="upload_task", + * ref="#/components/schemas/inline_response_200_5_upload_task", + * ), + * example={ + * "upload_task": { + * "id": "4328" + * } + * } + * ), + * ), + * @OA\Response( + * response=412, + * description="Precondition failed. An error code and message are returned.\n530 - Description file not present. Please upload the task description.\n531 - Internal error. Please contact api support team\n532 - Problem validating uploaded description file. The XML description format does not meet the standards\n533 - Task already exists.\n534 - Error creating the task.\n", + * @OA\JsonContent( + * ref="#/components/schemas/Error", + * ), + * ), + *) + */ public function task_upload() { $description = isset( $_FILES['description'] ) ? $_FILES['description'] : false; diff --git a/openml_OS/models/api/v1/Api_user.php b/openml_OS/models/api/v1/Api_user.php index 29446978b..efbb60ec3 100644 --- a/openml_OS/models/api/v1/Api_user.php +++ b/openml_OS/models/api/v1/Api_user.php @@ -18,40 +18,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { # http://test.openml.org/api/v1/user/list/uploader/1,2 - /** - *@OA\Get( - * path="/user/list", - * tags={"user"}, - * summary="List all users by user id", - * description="Returns an array with all user ids and names.", - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="API key to authenticate the user", - * required="false", - * ), - * @OA\Response( - * response=200, - * description="A list of users", - * @OA\JsonContent( - * ref="#/components/schemas/UserList", - * example={ - * "users":{ - * "user":[ - * { - * "id":"1", - * "username":"janvanrijn@gmail.com"}, - * { - * "id":"2", - * "username":"joaquin.vanschoren@gmail.com"} - * ] - * } - * } - * ), - * ), - *) - */ if (count($segments) >= 1 && $segments[0] == 'list') { array_shift($segments); $this->username_list($segments); @@ -117,6 +83,40 @@ function bootstrap($format, $segments, $request_type, $user_id) { } */ + /** + *@OA\Get( + * path="/user/list", + * tags={"user"}, + * summary="List all users by user id", + * description="Returns an array with all user ids and names.", + * @OA\Parameter( + * name="api_key", + * in="query", + * type="string", + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Response( + * response=200, + * description="A list of users", + * @OA\JsonContent( + * ref="#/components/schemas/UserList", + * example={ + * "users":{ + * "user":[ + * { + * "id":"1", + * "username":"janvanrijn@gmail.com"}, + * { + * "id":"2", + * "username":"joaquin.vanschoren@gmail.com"} + * ] + * } + * } + * ), + * ), + *) + */ private function username_list($segs) { # pass uploader list to get username list $legal_filters = array('user_id'); From cc278af5c37e9a2b3518f19fb3ffa5507710ca88 Mon Sep 17 00:00:00 2001 From: mwever Date: Wed, 8 Jul 2020 16:01:26 +0200 Subject: [PATCH 05/15] Fixed issues with doctrine. Now all the annotations get compiled correctly. --- openml_OS/models/api/v1/Api_data.php | 296 +++++++++++------- .../models/api/v1/Api_estimationprocedure.php | 8 +- openml_OS/models/api/v1/Api_evaluation.php | 26 +- .../models/api/v1/Api_evaluationmeasure.php | 8 +- openml_OS/models/api/v1/Api_flow.php | 101 ++++-- openml_OS/models/api/v1/Api_run.php | 147 ++++++--- openml_OS/models/api/v1/Api_setup.php | 75 +++-- openml_OS/models/api/v1/Api_study.php | 73 +++-- openml_OS/models/api/v1/Api_task.php | 105 ++++--- openml_OS/models/api/v1/Api_tasktype.php | 49 +-- openml_OS/models/api/v1/Api_user.php | 18 +- 11 files changed, 571 insertions(+), 335 deletions(-) diff --git a/openml_OS/models/api/v1/Api_data.php b/openml_OS/models/api/v1/Api_data.php index 2d662d40d..769664ce1 100644 --- a/openml_OS/models/api/v1/Api_data.php +++ b/openml_OS/models/api/v1/Api_data.php @@ -152,25 +152,30 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="Tags a dataset.", * @OA\Parameter( * name="data_id", - * in="formData", - * type="number", - * format="integer", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", * description="Api key to authenticate the user", * required=true, + * @OA\Schema( + * type="string" + * ) * ), * @OA\Response( * response=200, @@ -209,22 +214,28 @@ private function data_tag($data_id, $tag) { * description="Untags a dataset.", * @OA\Parameter( * name="data_id", - * in="formData", - * type="number", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -271,7 +282,9 @@ private function data_tag_untag($data_id,$tag, $do_untag) { * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified. /status/{status} - returns only datasets with a given status, either 'active', 'deactivated', or 'in_preparation'. @@ -283,7 +296,9 @@ private function data_tag_untag($data_id,$tag, $do_untag) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -294,13 +309,13 @@ private function data_tag_untag($data_id,$tag, $do_untag) { * ref="#/components/schemas/DataList", * example={ * "data": { - * "dataset": [ + * "dataset": { * { * "did":"1", * "name":"anneal", * "status":"active", * "format":"ARFF", - * "quality":[ + * "quality":{ * { * "name":"MajorityClassSize", * "value":"684" @@ -345,9 +360,9 @@ private function data_tag_untag($data_id,$tag, $do_untag) { * "name":"NumberOfSymbolicFeatures", * "value":"32" * } - * ] + * } * } - * ] + * } * } * } * ), @@ -363,19 +378,19 @@ private function data_tag_untag($data_id,$tag, $do_untag) { */ private function data_list($segs) { $legal_filters = array('tag', 'status', 'limit', 'offset', 'data_id', 'data_name', 'data_version', 'uploader', 'number_instances', 'number_features', 'number_classes', 'number_missing_values'); - + list($query_string, $illegal_filters) = $this->parse_filters($segs, $legal_filters); if (count($illegal_filters) > 0) { $this->returnError(370, $this->version, $this->openmlGeneralErrorCode, 'Legal filter operators: ' . implode(',', $legal_filters) .'. Found illegal filter(s): ' . implode(', ', $illegal_filters)); return; } - + $illegal_filter_inputs = $this->check_filter_inputs($query_string, $legal_filters, array('tag', 'status', 'data_name', 'number_instances', 'number_features', 'number_classes', 'number_missing_values')); if (count($illegal_filter_inputs) > 0) { $this->returnError(371, $this->version, $this->openmlGeneralErrorCode, 'Filters with illegal values: ' . implode(',', $illegal_filter_inputs)); return; } - + $tag = element('tag', $query_string, null); $name = element('data_name', $query_string, null); $data_id = element('data_id', $query_string, null); @@ -388,7 +403,7 @@ private function data_list($segs) { $nr_feats = element('number_features', $query_string, null); $nr_class = element('number_classes', $query_string, null); $nr_miss = element('number_missing_values', $query_string, null); - + if ($offset && !$limit) { $this->returnError(373, $this->version); return; @@ -407,17 +422,17 @@ private function data_list($segs) { $status_sql_variable = 'IFNULL(`s`.`status`, \'' . $this->config->item('default_dataset_status') . '\')'; $where_status = $status === null ? ' AND ' . $status_sql_variable . ' = "active" ' : ($status != "all" ? ' AND ' . $status_sql_variable . ' = "'. $status . '" ' : ''); $where_total = $where_tag . $where_did . $where_name . $where_version . $where_uploader . $where_insts . $where_feats . $where_class . $where_miss . $where_status; - + $where_limit = $limit === null ? '' : ' LIMIT ' . $limit; if($limit && $offset){ $where_limit = ' LIMIT ' . $offset . ',' . $limit; } - + $sql = 'SELECT d.*, ' . $status_sql_variable . ' AS `status` '. - 'FROM dataset d ' . + 'FROM dataset d ' . 'LEFT JOIN (SELECT `did`, MAX(`status`) AS `status` FROM `dataset_status` GROUP BY `did`) s ON d.did = s.did ' . 'WHERE (visibility = "public" or uploader='.$this->user_id.') '. $where_total . $where_limit; - + $datasets_res = $this->Dataset->query($sql); if( is_array( $datasets_res ) == false || count( $datasets_res ) == 0 ) { $this->returnError( 372, $this->version ); @@ -432,12 +447,12 @@ private function data_list($segs) { } # JvR: This is a BAD idea and this will break in the future, when OpenML grows. - $sql = + $sql = 'SELECT data, quality, value FROM data_quality ' . 'WHERE `data` IN (' . implode(',', array_keys($datasets)) . ') ' . 'AND evaluation_engine_id = ' . $this->config->item('default_evaluation_engine_id') . ' ' . - 'AND quality IN ("' . implode('","', $this->config->item('basic_qualities')) . '") ' . - 'AND value IS NOT NULL ' . + 'AND quality IN ("' . implode('","', $this->config->item('basic_qualities')) . '") ' . + 'AND value IS NOT NULL ' . 'ORDER BY `data`;'; $dq = $this->Data_quality->query($sql); @@ -459,15 +474,18 @@ private function data_list($segs) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=false, * ), @@ -489,10 +507,10 @@ private function data_list($segs) { * "file_id": "1", * "default_target_attribute": "class", * "version_label": "2", - * "tag": [ + * "tag": { * "study_1", * "uci" - * ], + * }, * "visibility": "public", * "original_data_url": "https://www.openml.org/d/2", * "status": "active", @@ -548,7 +566,7 @@ private function data($data_id) { foreach( $this->xml_fields_dataset['csv'] as $field ) { $dataset->{$field} = getcsv( $dataset->{$field} ); } - + $data_processed = $this->Data_processed->getById(array($data_id, $this->config->item('default_evaluation_engine_id'))); $relevant_fields = array('processing_date', 'error', 'warning'); foreach ($relevant_fields as $field) { @@ -558,7 +576,7 @@ private function data($data_id) { $dataset->{$field} = null; } } - + $dataset->status = $this->config->item('default_dataset_status'); $data_status = $this->Dataset_status->getWhereSingle('did =' . $data_id, 'status_date DESC'); if ($data_status != false) { @@ -567,7 +585,7 @@ private function data($data_id) { $this->xmlContents( 'data-get', $this->version, $dataset ); } - + private function data_reset($data_id) { $dataset = $this->Dataset->getById($data_id); if ($dataset == false) { @@ -579,7 +597,7 @@ private function data_reset($data_id) { $this->returnError(1022, $this->version); return; } - + $result = $this->Data_processed->deleteWhere('`did` = "' . $dataset->did . '" '); if ($result == false) { @@ -598,15 +616,18 @@ private function data_reset($data_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -691,22 +712,28 @@ private function data_delete($data_id) { * description="Uploads a dataset. Upon success, it returns the data id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/data).", * required=true, * ), * @OA\Parameter( * name="dataset", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="The actual dataset, being an ARFF file.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -816,7 +843,7 @@ private function data_upload() { $this->returnError(145, $this->version, $this->openmlGeneralErrorCode, 'Arff error in dataset file: ' . $uploadedFileCheck); return; } - + $to_folder = $this->data_folders['dataset']; $file_id = $this->File->register_uploaded_file($_FILES['dataset'], $to_folder, $this->user_id, 'dataset', $access_control); if ($file_id === false) { @@ -860,7 +887,7 @@ private function data_upload() { 'isOriginal' => 'true', 'file_id' => $file_id ); - + // extract all other necessary info from the XML description $dataset = all_tags_from_xml( $xml->children('oml', true), @@ -881,15 +908,15 @@ private function data_upload() { $this->returnError(134, $this->version); return; } - - // try to move the file to a new directory. If it fails, the dataset is + + // try to move the file to a new directory. If it fails, the dataset is // still valid, but we probably want to make some mechanism to inform administrators if ($file_record->type != 'url') { $subdirectory = floor($id / $this->content_folder_modulo) * $this->content_folder_modulo; $to_folder = $this->data_folders['dataset'] . '/' . $subdirectory . '/' . $id . '/'; $this->File->move_file($file_id, $to_folder); } - + // try making the ES stuff try { // update elastic search index. @@ -923,23 +950,28 @@ private function data_upload() { * description="Change the status of a dataset, either 'active' or 'deactivated'", * @OA\Parameter( * name="data_id", - * in="formData", - * type="number", - * format="integer", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="status", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="The status on which to filter the results, either 'active' or 'deactivated'.", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -959,7 +991,7 @@ private function status_update($data_id, $status) { $this->returnError(691, $this->version); return; } - + $dataset = $this->Dataset->getById($data_id); if ($dataset == false) { $this->returnError(692, $this->version); @@ -970,12 +1002,12 @@ private function status_update($data_id, $status) { $this->returnError(693, $this->version); return; } - + if ($status == 'active' && !$this->user_has_admin_rights) { $this->returnError(696, $this->version); return; } - + $status_record = $this->Dataset_status->getWhereSingle('did = ' . $data_id, 'status DESC'); $in_preparation = $this->config->item('default_dataset_status'); if ($status_record == false) { @@ -983,14 +1015,14 @@ private function status_update($data_id, $status) { } else { $old_status = $status_record->status; } - + $record = array( 'did' => $data_id, - 'status' => $status, + 'status' => $status, 'status_date' => now(), 'user_id' => $this->user_id ); - + if ( ($old_status == $in_preparation && $status == 'active') || ($old_status == $in_preparation && $status == 'deactivated') || @@ -999,10 +1031,10 @@ private function status_update($data_id, $status) { $this->Dataset_status->insert($record); } elseif ($old_status == 'deactivated' && $status == 'active') { $this->Dataset_status->delete(array($data_id, 'deactivated')); - + // see if the dataset is still active $status_record = $this->Dataset_status->getWhereSingle('did = ' . $data_id, 'status DESC'); - + $result = true; if (!$status_record || $status_record->status != 'active') { $result = $this->Dataset_status->insert($record); @@ -1015,7 +1047,7 @@ private function status_update($data_id, $status) { $this->returnError(694, $this->version); return; } - + $this->xmlContents('data-status-update', $this->version, array('did' => $data_id, 'status' => $status)); } @@ -1028,15 +1060,18 @@ private function status_update($data_id, $status) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=false, * ), @@ -1047,7 +1082,7 @@ private function status_update($data_id, $status) { * ref="#/components/schemas/DataFeatures", * example={ * "data_features": { - * "feature": [ + * "feature": { * { * "index": "0", * "name": "sepallength", @@ -1088,7 +1123,7 @@ private function status_update($data_id, $status) { * "is_ignore": "false", * "is_row_identifier": "false" * } - * ] + * } * } * } * ), @@ -1133,12 +1168,12 @@ private function data_features($data_id) { } } $dataset->index_values = $index_values; - + if ($data_processed->error && $dataset->features === false) { $this->returnError(274, $this->version); return; } - + if ($dataset->features === false) { $this->returnError(272, $this->version); return; @@ -1163,15 +1198,19 @@ private function data_features($data_id) { * description="Uploads dataset feature description. Upon success, it returns the data id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.features) and an [XML example](https://www.openml.org/api/v1/xml_example/data.features).", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -1195,11 +1234,11 @@ private function data_features_upload() { $this->returnError(442, $this->version); return; } - + // get description from file upload. Note that we will check the XSD later on (after we have assembled fields for error handling) $description = $_FILES['description']; $xml = simplexml_load_file($description['tmp_name']); - + // precheck XSD (if this pre-check succeeds, we can do database error logging later) if (!($xml->children('oml', true)->{'did'} && $xml->children('oml', true)->{'evaluation_engine_id'})) { $this->returnError(443, $this->version, $this->openmlGeneralErrorCode, 'XML misses basic fields did or evaluation_engine_id'); @@ -1218,13 +1257,13 @@ private function data_features_upload() { $this->returnError(444, $this->version); return; } - + $data_processed_record = $this->Data_processed->getById(array($did, $eval_id)); if ($data_processed_record && $data_processed_record->error == null) { $this->returnError(441, $this->version); return; } - + $num_tries = 0; if ($data_processed_record) { $num_tries = $data_processed_record->num_tries; @@ -1234,12 +1273,12 @@ private function data_features_upload() { $data = array('did' => $did, 'evaluation_engine_id' => $eval_id, 'user_id' => $this->user_id, - 'processing_date' => now(), + 'processing_date' => now(), 'num_tries' => $num_tries + 1); if ($xml->children('oml', true)->{'error'}) { $data['error'] = htmlentities($xml->children('oml', true)->{'error'}); } - + if (validateXml($description['tmp_name'], xsd('openml.data.features', $this->controller, $this->version), $xmlErrors) == false) { $data['error'] = 'XSD does not comply. XSD errors: ' . $xmlErrors; $success = $this->Data_processed->replace($data); @@ -1254,10 +1293,10 @@ private function data_features_upload() { } $this->db->trans_start(); - + # replace is delete then insert again $success = $this->Data_processed->replace($data); - if (!$success) { + if (!$success) { $this->returnError(445, $this->version, $this->openmlGeneralErrorCode, 'Failed to create data processed record. '); return; } @@ -1290,7 +1329,7 @@ private function data_features_upload() { if (in_array($feature['name'], $ignores)) { $feature['is_ignore'] = 'true'; } - + if (in_array('ClassDistribution', $feature)) { // check class distributions field json_decode($feature['ClassDistribution']); @@ -1300,7 +1339,7 @@ private function data_features_upload() { return; } } - + //actual insert of the feature if (array_key_exists('nominal_value', $feature)) { $nominal_values = $feature['nominal_value']; @@ -1308,19 +1347,19 @@ private function data_features_upload() { } else { $nominal_values = false; } - + $result = $this->Data_feature->insert($feature); if (!$result) { $this->db->trans_rollback(); $this->returnError(450, $this->version, $this->openmlGeneralErrorCode, 'feature: ' . $feature['name']); return; } - + if ($nominal_values) { // check the nominal value property foreach ($nominal_values as $value) { $data = array( - 'did' => $did, + 'did' => $did, 'index' => $feature['index'], 'value' => $value ); @@ -1331,7 +1370,7 @@ private function data_features_upload() { return; } } - + if ($feature['data_type'] != 'nominal') { // only allowed for nominal values $this->db->trans_rollback(); @@ -1371,7 +1410,9 @@ private function data_features_upload() { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -1382,7 +1423,7 @@ private function data_features_upload() { * ref="#/components/schemas/DataQualityList", * example={ * "data_qualities_list":{ - * "quality":[ + * "quality":{ * "NumberOfClasses", * "NumberOfFeatures", * "NumberOfInstances", @@ -1390,7 +1431,7 @@ private function data_features_upload() { * "NumberOfMissingValues", * "NumberOfNumericFeatures", * "NumberOfSymbolicFeatures" - * ] + * } * } * } * ), @@ -1443,15 +1484,18 @@ private function feature_qualities_list() { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the dataset.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=false, * ), @@ -1462,7 +1506,7 @@ private function feature_qualities_list() { * ref="#/components/schemas/DataQualities", * example={ * "data_qualities": { - * "quality": [ + * "quality": { * { * "name": "ClassCount", * "value": "3.0" @@ -1499,7 +1543,7 @@ private function feature_qualities_list() { * "name": "NumberOfSymbolicFeatures", * "value": "0" * } - * ] + * } * } * } * ), @@ -1539,7 +1583,7 @@ private function data_qualities($data_id, $evaluation_engine_id) { $interval_start = false; // $this->input->get( 'interval_start' ); $interval_end = false; // $this->input->get( 'interval_end' ); $interval_size = false; // $this->input->get( 'interval_size' ); - + if($interval_start !== false || $interval_end !== false || $interval_size !== false) { $interval_constraints = ''; if( $interval_start !== false && is_numeric( $interval_start ) ) { @@ -1555,7 +1599,7 @@ private function data_qualities($data_id, $evaluation_engine_id) { } else { $dataset->qualities = $this->Data_quality->getWhere('data = "' . $dataset->did . '" AND evaluation_engine_id = ' . $evaluation_engine_id); } - + if($data_processed->error && $dataset->qualities === false) { $this->returnError(364, $this->version, $this->openmlGeneralErrorCode, $data_processed->error); return; @@ -1632,15 +1676,19 @@ private function feature_qualities($data_id, $evaluation_engine_id) { * description="Uploads dataset qualities. Upon success, it returns the data id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.qualities) and an [XML example](https://www.openml.org/api/v1/xml_example/data.qualities).", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -1759,7 +1807,7 @@ private function data_qualities_upload() { $result = $this->Data_quality->insert_ignore($data); } } - + if ($this->db->trans_status() === FALSE) { $this->db->trans_rollback(); $this->returnError(389, $this->version); @@ -1788,21 +1836,27 @@ private function data_qualities_upload() { * @OA\Parameter( * name="data_engine_id", * in="path", - * type="string", + * @OA\Schema( + * type="integer" + * ), * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", * required=true, * ), * @OA\Parameter( * name="order", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -1811,7 +1865,7 @@ private function data_qualities_upload() { * description="A list of unprocessed datasets", * @OA\JsonContent( * ref="#/components/schemas/DataUnprocessed", - * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} + * example={"data_unprocessed": {"run": {{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}}}} * ), * ), * @OA\Response( @@ -1828,7 +1882,7 @@ private function data_unprocessed($evaluation_engine_id, $order) { $this->returnError(106, $this->version); return; } - + $this->db->select('d.*')->from('dataset d'); $this->db->join('data_processed p', 'd.did = p.did AND evaluation_engine_id = ' . $evaluation_engine_id, 'left'); $this->db->where('(p.did IS NULL OR (p.error IS NOT NULL AND p.num_tries < ' . $this->config->item('process_data_tries') . ' AND p.processing_date < "' . now_offset('-' . $this->config->item('process_data_offset')) . '"))'); @@ -1872,28 +1926,36 @@ private function data_unprocessed($evaluation_engine_id, $order) { * @OA\Parameter( * name="data_engine_id", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", * required=true, * ), * @OA\Parameter( * name="order", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), * @OA\Parameter( * name="qualities", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Comma-separated list of (at least two) quality names, e.g. 'NumberOfInstances,NumberOfFeatures'.", * required=true, * ), @@ -1902,7 +1964,7 @@ private function data_unprocessed($evaluation_engine_id, $order) { * description="A list of unprocessed datasets", * @OA\JsonContent( * ref="#/components/schemas/DataUnprocessed", - * example={"data_unprocessed": {"run": [{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}]}} + * example={"data_unprocessed": {"run": {{"did": "1", "status": "deactivated", "version": "2", "name": "anneal", "format": "ARFF"}}}} * ), * ), * @OA\Response( diff --git a/openml_OS/models/api/v1/Api_estimationprocedure.php b/openml_OS/models/api/v1/Api_estimationprocedure.php index 30012c80c..9420cf44b 100644 --- a/openml_OS/models/api/v1/Api_estimationprocedure.php +++ b/openml_OS/models/api/v1/Api_estimationprocedure.php @@ -51,7 +51,9 @@ private function estimationprocedure($id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -62,7 +64,7 @@ private function estimationprocedure($id) { * ref="#/components/schemas/EstimationProcedureList", * example={ * "estimationprocedures": { - * "estimationprocedure": [ + * "estimationprocedure": { * { * "id":"1", * "ttid":"1", @@ -81,7 +83,7 @@ private function estimationprocedure($id) { * "folds":"2", * "stratified_sampling":"true" * } - * ] + * } * } * } * ), diff --git a/openml_OS/models/api/v1/Api_evaluation.php b/openml_OS/models/api/v1/Api_evaluation.php index 58b21c419..e489cace6 100644 --- a/openml_OS/models/api/v1/Api_evaluation.php +++ b/openml_OS/models/api/v1/Api_evaluation.php @@ -50,21 +50,27 @@ function bootstrap($format, $segments, $request_type, $user_id) { * @OA\Parameter( * name="evaluation_engine_id", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="The ID of the evaluation engine. You get this ID when you register a new evaluation engine with OpenML. The ID of the main evaluation engine is 1.", * required=true, * ), * @OA\Parameter( * name="order", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="When there are multiple runs still to evaluate, this defines which one to return. Options are 'normal' - the oldest run, 'reverse' - the newest run, or 'random' - a random run.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -73,7 +79,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="A list of evaluations descriptions", * @OA\JsonContent( * ref="#/components/schemas/EvaluationRequest", - * example={"evaluation_request": {"run": [{"setup_id": "68799271", "upload_time": "2018-04-03 21:05:38", "uploader": "1935", "task_id": "3021", "run_id": "8943712"}]}} + * example={"evaluation_request": {"run": {{"setup_id": "68799271", "upload_time": "2018-04-03 21:05:38", "uploader": "1935", "task_id": "3021", "run_id": "8943712"}}}} * ), * ), * @OA\Response( @@ -148,7 +154,9 @@ private function evaluation_request($evaluation_engine_id, $order, $num_request * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /function/{name} - name of the evaluation measure, e.g. area_under_auc or predictive_accuracy. See the OpenML website for the complete list of measures. /tag/{tag} - returns only evaluations of runs tagged with the given tag. @@ -166,7 +174,9 @@ private function evaluation_request($evaluation_engine_id, $order, $num_request * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -175,12 +185,12 @@ private function evaluation_request($evaluation_engine_id, $order, $num_request * description="A list of evaluations descriptions", * @OA\JsonContent( * ref="#/components/schemas/EvaluationList", - * example={"evaluations": {"evaluation": [{"function": "area_under_roc_curve", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]", "value": "0.839359", "uploader": "1", "flow_id": "61"}, {"function": "f_measure", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]", "value": "0.600026", "uploader": "1", "flow_id": "61"}, {"function": "predictive_accuracy", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": [], "value": "0.614634", "uploader": "1", "flow_id": "61"}]}} + * example={"evaluations": {"evaluation": {{"function": "area_under_roc_curve", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]", "value": "0.839359", "uploader": "1", "flow_id": "61"}, {"function": "f_measure", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": "[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]", "value": "0.600026", "uploader": "1", "flow_id": "61"}, {"function": "predictive_accuracy", "upload_time": "2014-04-06 23:30:40", "task_id": "68", "run_id": "1", "array_data": {}, "value": "0.614634", "uploader": "1", "flow_id": "61"}}}} * ), * ), * @OA\Response( * response=412, - * description="Precondition failed. An error code and message are returned.\n540 - Please provide at least task, flow or setup, uploader or run, to\nfilter results, or limit the number of responses.\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n542 - There where no results. Check whether there are runs under the given constraint.\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n544 - Illegal filter specified.\n545 - Offset specified without limit.\n546 - Requested result limit too high.\n547 - Per fold can only be set to value "true" or "false".\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\n", + * description="Precondition failed. An error code and message are returned.\n540 - Please provide at least task, flow or setup, uploader or run, to\nfilter results, or limit the number of responses.\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n542 - There where no results. Check whether there are runs under the given constraint.\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n544 - Illegal filter specified.\n545 - Offset specified without limit.\n546 - Requested result limit too high.\n547 - Per fold can only be set to value 'true' or 'false'.\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\n", * @OA\JsonContent( * ref="#/components/schemas/Error", * ), diff --git a/openml_OS/models/api/v1/Api_evaluationmeasure.php b/openml_OS/models/api/v1/Api_evaluationmeasure.php index e554ea3e2..4ad961f65 100644 --- a/openml_OS/models/api/v1/Api_evaluationmeasure.php +++ b/openml_OS/models/api/v1/Api_evaluationmeasure.php @@ -34,7 +34,9 @@ function bootstrap($format, $segments, $request_type, $user_id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -46,12 +48,12 @@ function bootstrap($format, $segments, $request_type, $user_id) { * example={ * "evaluation_measures":{ * "measures":{ - * "measure":[ + * "measure":{ * "area_under_roc_curve", * "average_cost", * "binominal_test", * "build_cpu_time" - * ] + * } * } * } * } diff --git a/openml_OS/models/api/v1/Api_flow.php b/openml_OS/models/api/v1/Api_flow.php index cd6b1a31f..8302a8dae 100644 --- a/openml_OS/models/api/v1/Api_flow.php +++ b/openml_OS/models/api/v1/Api_flow.php @@ -92,7 +92,9 @@ function bootstrap($format, $segments, $request_type, $user_id) { * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. /tag/{tag} - returns only tasks tagged with the given tag. @@ -103,7 +105,9 @@ function bootstrap($format, $segments, $request_type, $user_id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -115,7 +119,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { * example={ * "flows": * { - * "flow":[ + * "flow":{ * { * "id":"65", * "full_name":"weka.RandomForest(1)", @@ -140,7 +144,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { * "external_version":"Weka_3.7.10_8034", * "uploader":"1" * } - * ] + * } * } * } * ), @@ -215,23 +219,28 @@ private function flow_list($segs) { * description="Tags a flow.", * @OA\Parameter( * name="flow_id", - * in="formData", - * type="number", - * format="integer", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the flow.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -272,22 +281,28 @@ private function flow_tag($flow_id, $tag) { * description="Untags a flow.", * @OA\Parameter( * name="flow_id", - * in="formData", - * type="number", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the flow.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -335,21 +350,27 @@ private function flow_tag_untag($flow_id, $tag, $do_untag) { * @OA\Parameter( * name="name", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="The name of the flow.", * required=true, * ), * @OA\Parameter( * name="version", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="The external version of the flow", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -405,15 +426,18 @@ private function flow_exists($name, $external_version) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the flow.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -433,11 +457,11 @@ private function flow_exists($name, $external_version) { * "upload_date":"2014-04-23 18:00:36", * "language":"Java", * "dependencies":"Weka_3.7.5", - * "parameter": [ + * "parameter": { * { * "name":"A", * "data_type":"flag", - * "default_value":[], + * "default_value":{}, * "description":"Laplace smoothing..." * }, * { @@ -446,7 +470,7 @@ private function flow_exists($name, $external_version) { * "default_value":"0.25", * "description":"Set confidence threshold..." * } - * ] + * } * } * } * ), @@ -485,22 +509,28 @@ private function flow($id) { * description="Uploads a flow. Upon success, it returns the flow id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", * required=true, * ), * @OA\Parameter( * name="flow", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="The actual flow, being a source (or binary) file.", * required=false, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -664,15 +694,18 @@ private function flow_upload() { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the flow.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=true, * ), diff --git a/openml_OS/models/api/v1/Api_run.php b/openml_OS/models/api/v1/Api_run.php index 85f91cf6e..ad3ae30e7 100644 --- a/openml_OS/models/api/v1/Api_run.php +++ b/openml_OS/models/api/v1/Api_run.php @@ -111,23 +111,28 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="Tags a run.", * @OA\Parameter( * name="run_id", - * in="formData", - * type="number", - * format="integer", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the run.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -168,22 +173,28 @@ private function run_tag($run_id, $tag) { * description="Untags a run.", * @OA\Parameter( * name="run_id", - * in="formData", - * type="number", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the run.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -229,7 +240,9 @@ private function run_tag_untag($run_id, $tag, $do_untag) { * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /tag/{tag} - return only runs tagged with the given tag. /run/{ids} - return only specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3'' @@ -244,7 +257,9 @@ private function run_tag_untag($run_id, $tag, $do_untag) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -253,7 +268,7 @@ private function run_tag_untag($run_id, $tag, $do_untag) { * description="A list of runs descriptions", * @OA\JsonContent( * ref="#/components/schemas/RunList", - * example={"runs": {"run": [{"upload_time": "2014-04-06 23:30:40", "task_id": "28", "run_id": "100", "error_message": [], "setup_id": "12", "uploader": "1", "flow_id": "67"}, {"upload_time": "2014-04-06 23:30:40", "task_id": "48", "run_id": "101", "error_message": [], "setup_id": "6", "uploader": "1", "flow_id": "61"}, {"upload_time": "2014-04-06 23:30:40", "task_id": "41", "run_id": "102", "error_message": [], "setup_id": "3", "uploader": "1", "flow_id": "58"}]}} + * example={"runs": {"run": {{"upload_time": "2014-04-06 23:30:40", "task_id": "28", "run_id": "100", "error_message": {}, "setup_id": "12", "uploader": "1", "flow_id": "67"}, {"upload_time": "2014-04-06 23:30:40", "task_id": "48", "run_id": "101", "error_message": {}, "setup_id": "6", "uploader": "1", "flow_id": "61"}, {"upload_time": "2014-04-06 23:30:40", "task_id": "41", "run_id": "102", "error_message": {}, "setup_id": "3", "uploader": "1", "flow_id": "58"}}}} * ), * ), * @OA\Response( @@ -375,15 +390,18 @@ private function run_list($segs, $user_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the run.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -403,7 +421,7 @@ private function run_list($segs, $user_id) { * "flow_id":"67", * "flow_name":"weka.BayesNet_K2(1)", * "setup_string":"weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", - * "parameter_setting": [ + * "parameter_setting": { * { * "name":"D", * "value":"true" @@ -420,7 +438,7 @@ private function run_list($segs, $user_id) { * "name":"S", * "value":"BAYES" * } - * ], + * }, * "input_data": * { * "dataset": @@ -432,7 +450,7 @@ private function run_list($segs, $user_id) { * }, * "output_data": * { - * "file": [ + * "file": { * { * "did":"48838", * "file_id":"261", @@ -445,8 +463,8 @@ private function run_list($segs, $user_id) { * "name":"predictions", * "url":"https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff" * } - * ], - * "evaluation": [ + * }, + * "evaluation": { * { * "name":"area_under_roc_curve", * "flow_id":"4", @@ -469,7 +487,7 @@ private function run_list($segs, $user_id) { * "flow_id":"13", * "value":"0.913601" * } - * ] + * } * } * } * } @@ -519,15 +537,18 @@ private function run($run_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the run.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=true, * ), @@ -610,14 +631,18 @@ private function run_delete($run_id) { * @OA\Parameter( * name="id", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Run ID.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -676,36 +701,46 @@ private function run_reset($run_id) { * description="Uploads a run. Upon success, it returns the run id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", * required=true, * ), * @OA\Parameter( * name="predictions", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="The predictions generated by the run", * required=true, * ), * @OA\Parameter( * name="model_readable", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="The human-readable model generated by the run", * required=false, * ), * @OA\Parameter( * name="model_serialized", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="The serialized model generated by the run", * required=false, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -1045,15 +1080,18 @@ private function run_upload() { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the run.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -1123,22 +1161,27 @@ private function run_trace($run_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the run.", * required=true, * ), * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -1225,15 +1268,19 @@ private function run_trace_upload() { * description="Uploads a run evaluation. When successful, it returns the run id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the run evaluation.Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.evaluate) and an [XML example](https://www.openml.org/api/v1/xml_example/run.evaluate).", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), diff --git a/openml_OS/models/api/v1/Api_setup.php b/openml_OS/models/api/v1/Api_setup.php index c056236c8..05222726a 100644 --- a/openml_OS/models/api/v1/Api_setup.php +++ b/openml_OS/models/api/v1/Api_setup.php @@ -92,23 +92,28 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="Tags a setup.", * @OA\Parameter( * name="setup_id", - * in="formData", - * type="number", - * format="integer", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the setup.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -149,22 +154,28 @@ private function setup_tag($setup_id, $tag) { * description="Untags a setup.", * @OA\Parameter( * name="setup_id", - * in="formData", - * type="number", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the setup.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -210,15 +221,18 @@ private function setup_tag_untag($setup_id, $tag, $do_untag) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -230,7 +244,7 @@ private function setup_tag_untag($setup_id, $tag, $do_untag) { * example={ * "setup_parameters":{ * "flow_id":"59", - * "parameter":[ + * "parameter":{ * { * "full_name":"weka.JRip(1)_F", * "parameter_name":"F", @@ -255,7 +269,7 @@ private function setup_tag_untag($setup_id, $tag, $do_untag) { * "data_type":"option", * "default_value":"1", * "value":"1" - * }] + * }} * } * } * ), @@ -300,7 +314,9 @@ private function setup($setup_id) { * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /tag/{tag} - returns only setups tagged with the given tag. /flow/{ids} - return only setups for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3'' @@ -312,7 +328,9 @@ private function setup($setup_id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -323,11 +341,11 @@ private function setup($setup_id) { * ref="#/components/schemas/SetupList", * example={ * "setups": { - * "setup": [ + * "setup": { * { * "setup_id":"10", * "flow_id":"65", - * "parameter": [ + * "parameter": { * { * "id":"4144", * "flow_id":"65", @@ -348,9 +366,9 @@ private function setup($setup_id) { * "default_value":"0", * "value":"0" * } - * ] + * } * } - * ] + * } * } * } * ), @@ -547,15 +565,18 @@ private function setup_exists($partial) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the setup.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), diff --git a/openml_OS/models/api/v1/Api_study.php b/openml_OS/models/api/v1/Api_study.php index 94d921cf9..6b2d3d333 100644 --- a/openml_OS/models/api/v1/Api_study.php +++ b/openml_OS/models/api/v1/Api_study.php @@ -67,15 +67,19 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="Creates a new study. Upon success, it returns the study id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the study. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.study.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/study).", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -255,22 +259,27 @@ private function status_update($study_id, $status) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the study. Supplied in the URL path.", * required=true, * ), * @OA\Parameter( * name="ids", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -314,22 +323,27 @@ private function study_attach($study_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the study.", * required=true, * ), * @OA\Parameter( * name="ids", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -442,15 +456,18 @@ private function study_attach_detach($study_id, $attach) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the study.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -525,7 +542,9 @@ private function study_delete($study_id) { * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /main_entity_type/{type} - only return studies collecting entities of a given type (e.g. 'task' or 'run'). /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3'' @@ -536,7 +555,9 @@ private function study_delete($study_id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -547,7 +568,7 @@ private function study_delete($study_id) { * ref="#/components/schemas/StudyList", * example={ * "study_list":{ - * "study":[ + * "study":{ * { * "id":"1", * "alias":"Study_1", @@ -562,7 +583,7 @@ private function study_delete($study_id) { * "creation_date":"2017-07-20 15:51:20", * "creator":"2" * } - * ] + * } * } * } * ), @@ -639,14 +660,18 @@ private function study_list($segs) { * @OA\Parameter( * name="id", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="ID or alias of the study.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=false, * ), @@ -663,8 +688,8 @@ private function study_list($segs) { * "description": "CC18 benchmark suite", * "creation_date": "2019-02-16T17:35:58", * "creator": "1159", - * "data": {"data_id": ["1","2","3"]}, - * "tasks": {"task_id": ["1","2","3"]} + * "data": {"data_id": {"1","2","3"}}, + * "tasks": {"task_id": {"1","2","3"}} * } * } * ), diff --git a/openml_OS/models/api/v1/Api_task.php b/openml_OS/models/api/v1/Api_task.php index c1aaa2be3..a20f66822 100644 --- a/openml_OS/models/api/v1/Api_task.php +++ b/openml_OS/models/api/v1/Api_task.php @@ -79,23 +79,28 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="Tags a task.", * @OA\Parameter( * name="task_id", - * in="formData", - * type="number", - * format="integer", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the task.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -136,22 +141,28 @@ private function task_tag($task_id, $tag) { * description="Untags a task.", * @OA\Parameter( * name="task_id", - * in="formData", - * type="number", + * in="query", + * @OA\Schema( + * type="integer" + * ), * description="Id of the task.", * required=true, * ), * @OA\Parameter( * name="tag", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Tag name", * required=true, * ), * @OA\Parameter( * name="api_key", - * in="formData", - * type="string", + * in="query", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -197,7 +208,9 @@ private function task_tag_untag($task_id, $tag, $do_untag) { * @OA\Parameter( * name="filters", * in="path", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Any combination of these filters /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified. /status/{status} - returns only tasks with a given status, either 'active', 'deactivated', or 'in_preparation'. @@ -211,7 +224,9 @@ private function task_tag_untag($task_id, $tag, $do_untag) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -222,7 +237,7 @@ private function task_tag_untag($task_id, $tag, $do_untag) { * ref="#/components/schemas/TaskList", * example={ * "task": { - * "task": [ + * "task": { * { * "task_id":"1", * "task_type":"Supervised Classification", @@ -230,7 +245,7 @@ private function task_tag_untag($task_id, $tag, $do_untag) { * "name":"anneal", * "status":"active", * "format":"ARFF", - * "input":[ + * "input":{ * { * "name":"estimation_procedure", * "value":"1" @@ -247,8 +262,8 @@ private function task_tag_untag($task_id, $tag, $do_untag) { * "name":"target_feature", * "value":"class" * } - * ], - * "quality":[ + * }, + * "quality":{ * { * "name":"MajorityClassSize", * "value":"684" @@ -293,16 +308,16 @@ private function task_tag_untag($task_id, $tag, $do_untag) { * "name":"NumberOfSymbolicFeatures", * "value":"32" * } - * ], - * "tag":[ + * }, + * "tag":{ * "basic", * "study_1", * "study_7", * "under100k", * "under1m" - * ] + * } * } - * ] + * } * } * } * ), @@ -402,15 +417,18 @@ private function task_list($segs, $user_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the task.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=false, * ), @@ -423,7 +441,7 @@ private function task_list($segs, $user_id) { * "task": { * "task_id":"1", * "task_type":"Supervised Classification", - * "input":[ + * "input":{ * { * "name":"source_data", * "data_set":{ @@ -436,7 +454,7 @@ private function task_list($segs, $user_id) { * "estimation_procedure":{ * "type":"crossvalidation", * "data_splits_url":"https://www.openml.org/api_splits/get/1/Task_1_splits.arff", - * "parameter":[ + * "parameter":{ * { * "name":"number_repeats", * "value":"1" @@ -452,12 +470,12 @@ private function task_list($segs, $user_id) { * "name":"stratified_sampling", * "value":"true" * } - * ] + * } * } * }, * { * "name":"cost_matrix", - * "cost_matrix":[] + * "cost_matrix":{} * }, * { * "name":"evaluation_measures", @@ -466,12 +484,12 @@ private function task_list($segs, $user_id) { * "evaluation_measure":"predictive_accuracy" * } * } - * ], + * }, * "output":{ * "name":"predictions", * "predictions":{ * "format":"ARFF", - * "feature":[ + * "feature":{ * { * "name":"repeat", * "type":"integer" @@ -492,10 +510,10 @@ private function task_list($segs, $user_id) { * "name":"prediction", * "type":"string" * } - * ] + * } * } * }, - * "tag":["basic","study_1","under100k","under1m"] + * "tag":{"basic","study_1","under100k","under1m"} * } * } * ), @@ -565,15 +583,18 @@ private function task_inputs($task_id) { * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="Id of the task.", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), @@ -656,15 +677,19 @@ private function task_delete($task_id) { * description="Uploads a task. Upon success, it returns the task id.", * @OA\Parameter( * name="description", - * in="formData", - * type="file", + * in="query", + * @OA\Schema( + * type="file" + * ), * description="An XML file describing the task. Only name, description, and task format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.task.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/task).", * required=true, * ), * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="Api key to authenticate the user", * required=true, * ), diff --git a/openml_OS/models/api/v1/Api_tasktype.php b/openml_OS/models/api/v1/Api_tasktype.php index 19d382caf..9ead049d9 100644 --- a/openml_OS/models/api/v1/Api_tasktype.php +++ b/openml_OS/models/api/v1/Api_tasktype.php @@ -25,12 +25,14 @@ function bootstrap($format, $segments, $request_type, $user_id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", - * required="false", + * required=false, * ), * @OA\Response( - * response=default, + * response="default", * description="Unexpected error", * @OA\JsonContent( * ref="#/components/schemas/Error", @@ -43,7 +45,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { * ref="#/components/schemas/TaskTypeList", * example={ * "task_types":{ - * "task_type":[ + * "task_type":{ * { * "id":"1", * "name":"Supervised Classification", @@ -55,8 +57,8 @@ function bootstrap($format, $segments, $request_type, $user_id) { * "name":"Supervised Regression", * "description":"Given a dataset with a numeric target ...", * "creator":"Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl" - * },{} - * ] + * } + * } * } * } * ), @@ -75,19 +77,22 @@ function bootstrap($format, $segments, $request_type, $user_id) { * summary="Get task type description", * description="Returns information about a task type. The information includes a description, the given inputs and the expected outputs.", * @OA\Parameter( + * name="api_key", + * in="query", + * @OA\Schema( + * type="string" + * ), + * description="API key to authenticate the user", + * required=false, + * ), + * @OA\Parameter( * name="id", * in="path", - * type="number", - * format="integer", + * @OA\Schema( + * type="integer" + * ), * description="ID of the task.", - * required="true", - * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * type="string", - * description="Api key to authenticate the user", - * required="false", + * required=true, * ), * @OA\Response( * response=200, @@ -105,22 +110,22 @@ function bootstrap($format, $segments, $request_type, $user_id) { * You can also upload your own evaluation measures, provided that the code for doing so is available from the implementation used. For extremely large datasets, it may be infeasible to upload all predictions. In those cases, you need to compute and provide the evaluations yourself.

\ * \ * Optionally, you can upload the model trained on all the input data. There is no restriction on the file format, but please use a well-known format or PMML.", - * "creator": [ + * "creator": { * "Joaquin Vanschoren", * "Jan van Rijn", * "Luis Torgo", * "Bernd Bischl" - * ], - * "contributor": [ + * }, + * "contributor": { * "Bo Gao", * "Simon Fischer", * "Venkatesh Umaashankar", * "Michael Berthold", * "Bernd Wiswedel", * "Patrick Winter" - * ], + * }, * "creation_date": "2013-01-24 00:00:00", - * "input": [ + * "input": { * { * "name": "source_data", * "requirement": "required", @@ -148,7 +153,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { * "name": "evaluation_measures", * "data_type": "string" * } - * ] + * } * } * } * ), diff --git a/openml_OS/models/api/v1/Api_user.php b/openml_OS/models/api/v1/Api_user.php index efbb60ec3..0847865b0 100644 --- a/openml_OS/models/api/v1/Api_user.php +++ b/openml_OS/models/api/v1/Api_user.php @@ -92,7 +92,9 @@ function bootstrap($format, $segments, $request_type, $user_id) { * @OA\Parameter( * name="api_key", * in="query", - * type="string", + * @OA\Schema( + * type="string" + * ), * description="API key to authenticate the user", * required=false, * ), @@ -103,18 +105,20 @@ function bootstrap($format, $segments, $request_type, $user_id) { * ref="#/components/schemas/UserList", * example={ * "users":{ - * "user":[ + * "user":{ * { * "id":"1", - * "username":"janvanrijn@gmail.com"}, + * "username":"janvanrijn@gmail.com" + * }, * { * "id":"2", - * "username":"joaquin.vanschoren@gmail.com"} - * ] + * "username":"joaquin.vanschoren@gmail.com" + * } + * } * } * } - * ), - * ), + * ) + * ) *) */ private function username_list($segs) { From e7212d80f3e54ee0e51de886d5e1a7bffd91c806 Mon Sep 17 00:00:00 2001 From: mwever Date: Wed, 8 Jul 2020 18:30:46 +0200 Subject: [PATCH 06/15] Fixed issues with schemas and properties with array types requiring a type/ref for items. --- openml_OS/controllers/Api.php | 584 +++++++++++++++++++++++++--------- 1 file changed, 436 insertions(+), 148 deletions(-) diff --git a/openml_OS/controllers/Api.php b/openml_OS/controllers/Api.php index c17f2a0fa..a81b83ad9 100644 --- a/openml_OS/controllers/Api.php +++ b/openml_OS/controllers/Api.php @@ -25,7 +25,8 @@ function __construct() * description="ID of the untagged setup", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200", * @OA\Property( @@ -33,15 +34,21 @@ function __construct() * ref="#/components/schemas/inline_response_200_data_delete", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationMeasureList_evaluation_measures_measures", - * @OA\Property( + * @OA\Property( * property="measure", * type="array", + * description="The evaluation measure names", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_10_flow_exists", * @OA\Property( @@ -55,7 +62,8 @@ function __construct() * description="true or false", * ), *) - * + */ +/** * @OA\Schema( * schema="Data_data_set_description", * @OA\Property( @@ -92,6 +100,9 @@ function __construct() * property="tag", * type="array", * description="Tags added by OpenML users. Includes study tags in the form `study_1`", + * @OA\Items( + * type="string" + * ) * ), * @OA\Property( * property="visibility", @@ -139,7 +150,8 @@ function __construct() * description="The name of the dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="DataList_data_dataset", * @OA\Property( @@ -155,6 +167,9 @@ function __construct() * @OA\Property( * property="quality", * type="array", + * @OA\Items( + * ref="#/components/schemas/DataList_data_quality" + * ) * ), * @OA\Property( * property="name", @@ -167,12 +182,16 @@ function __construct() * description="The data format of the dataset, e.g. ARFF", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_estimation_procedure", * @OA\Property( * property="parameter", * type="array", + * @OA\Items( + * ref="#/components/schemas/Task_task_description_estimation_procedure_parameter" + * ) * ), * @OA\Property( * property="type", @@ -185,7 +204,8 @@ function __construct() * description="The url where the data splits can be downloaded", * ), *) - * + */ +/** * @OA\Schema( * schema="DataList_data_quality", * @OA\Property( @@ -199,7 +219,8 @@ function __construct() * description="The value of the property", * ), *) - * + */ +/** * @OA\Schema( * schema="FlowList_flows_flow", * @OA\Property( @@ -233,7 +254,8 @@ function __construct() * description="The flow name", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description_input_data_dataset", * @OA\Property( @@ -252,15 +274,20 @@ function __construct() * description="The name of the dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationList_evaluations", - * @OA\Property( + * @OA\Property( * property="evaluation", * type="array", + * @OA\Items( + * ref="#/components/schemas/EvaluationList_evaluations_evaluation" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_14_study_delete", * @OA\Property( @@ -269,7 +296,8 @@ function __construct() * description="ID of the deleted setup, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="DataUnprocessed", * type="object", @@ -278,7 +306,8 @@ function __construct() * ref="#/components/schemas/DataUnprocessed_data_unprocessed", * ), *) - * + */ +/** * @OA\Schema( * schema="DataUnprocessed_data_unprocessed_dataset", * @OA\Property( @@ -307,15 +336,20 @@ function __construct() * description="The dataset format, e.g. ARFF", * ), *) - * + */ +/** * @OA\Schema( * schema="EstimationProcedureList_estimationprocedures", * @OA\Property( * property="estimationprocedure", * type="array", + * @OA\Items( + * ref="#/components/schemas/EstimationProcedureList_estimationprocedures_estimationprocedure" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="TaskTypeList", * type="object", @@ -324,7 +358,8 @@ function __construct() * ref="#/components/schemas/TaskTypeList_task_types", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description_output_data_evaluation", * @OA\Property( @@ -348,7 +383,8 @@ function __construct() * description="The id of the code used to compute this evaluation method", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskList_task_quality", * @OA\Property( @@ -362,12 +398,16 @@ function __construct() * description="The value of the quality", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description", * @OA\Property( * property="input", * type="array", + * @OA\Items( + * ref="#/components/schemas/Task_task_description_input" + * ) * ), * @OA\Property( * property="task_type", @@ -377,6 +417,10 @@ function __construct() * @OA\Property( * property="tag", * type="array", + * description="Tags added by OpenML uers. Includes study tags in the form 'study_1'", + * @OA\Items( + * type="string" + * ) * ), * @OA\Property( * property="task_id", @@ -386,9 +430,13 @@ function __construct() * @OA\Property( * property="output", * type="array", + * @OA\Items( + * ref="#/components/schemas/Task_task_description_output" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_9_upload_flow", * @OA\Property( @@ -397,7 +445,8 @@ function __construct() * description="ID of the uploaded flow, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="Flow_flow_description", * @OA\Property( @@ -418,11 +467,17 @@ function __construct() * @OA\Property( * property="parameter", * type="array", + * @OA\Items( + * ref="#/components/schemas/Flow_flow_description_parameter" + * ) * ), * @OA\Property( * property="tag", * type="array", * description="Tags added by OpenML users. Includes study tags in the form `study_1`", + * @OA\Items( + * type="string" + * ) * ), * @OA\Property( * property="version", @@ -455,7 +510,8 @@ function __construct() * description="The name of the flow", * ), *) - * + */ +/** * @OA\Schema( * schema="Task", * type="object", @@ -464,7 +520,8 @@ function __construct() * ref="#/components/schemas/Task_task_description", * ), *) - * + */ +/** * @OA\Schema( * schema="Setup", * type="object", @@ -473,15 +530,20 @@ function __construct() * ref="#/components/schemas/Setup_setup_parameters", * ), *) - * + */ +/** * @OA\Schema( * schema="FlowList_flows", * @OA\Property( * property="flow", * type="array", + * @OA\Items( + * ref="#/components/schemas/FlowList_flows_flow" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="RunTrace", * type="object", @@ -490,7 +552,8 @@ function __construct() * ref="#/components/schemas/RunTrace_trace", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_predictions_feature", * @OA\Property( @@ -504,7 +567,8 @@ function __construct() * description="The name of the prediction feature, e.g. row_id", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_24_study_delete", * @OA\Property( @@ -513,7 +577,8 @@ function __construct() * description="ID of the deleted study, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_5_upload_task", * @OA\Property( @@ -522,7 +587,8 @@ function __construct() * description="ID of the uploaded task, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_13_flow_untag", * @OA\Property( @@ -531,15 +597,20 @@ function __construct() * description="ID of the untagged flow", * ), *) - * + */ +/** * @OA\Schema( * schema="UserList_users", * @OA\Property( * property="user", * type="array", + * @OA\Items( + * ref="#/components/schemas/UserList_users_user" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="DataFeatures", * type="object", @@ -548,12 +619,16 @@ function __construct() * ref="#/components/schemas/DataFeatures_data_features", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_predictions", * @OA\Property( * property="feature", * type="array", + * @OA\Items( + * ref="#/components/schemas/Task_task_description_predictions_feature" + * ) * ), * @OA\Property( * property="format", @@ -561,7 +636,8 @@ function __construct() * description="The fromat of the predictions, e.g. ARFF", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_15_flow_tag", * @OA\Property( @@ -570,12 +646,16 @@ function __construct() * description="ID of the tagged setup", * ), *) - * + */ +/** * @OA\Schema( * schema="Setup_setup_parameters", * @OA\Property( * property="parameter_setting", * type="array", + * @OA\Items( + * ref="#/components/schemas/Setup_setup_parameters_parameter_setting" + * ) * ), * @OA\Property( * property="flow_id", @@ -583,7 +663,8 @@ function __construct() * description="ID of the flow, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="DataList", * type="object", @@ -592,7 +673,8 @@ function __construct() * ref="#/components/schemas/DataList_data", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_26_study_attach", * @OA\Property( @@ -611,7 +693,8 @@ function __construct() * description="ID of the study, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_2_data_tag", * @OA\Property( @@ -620,7 +703,8 @@ function __construct() * description="ID of the tagged dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_12", * @OA\Property( @@ -628,7 +712,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_12_flow_tag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_13", * @OA\Property( @@ -636,7 +721,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_13_flow_untag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_10", * @OA\Property( @@ -644,7 +730,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_10_flow_exists", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_23_upload_flow", * @OA\Property( @@ -653,7 +740,8 @@ function __construct() * description="ID of the run with the trace, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_16", * @OA\Property( @@ -661,7 +749,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_16_flow_untag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_17", * @OA\Property( @@ -669,7 +758,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_17_data_delete", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_14", * @OA\Property( @@ -677,7 +767,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_14_study_delete", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_15", * @OA\Property( @@ -685,7 +776,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_15_flow_tag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_18", * @OA\Property( @@ -693,7 +785,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_18_upload_flow", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_19", * @OA\Property( @@ -701,7 +794,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_19_run_tag", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationRequest_evaluation_request", * @OA\Property( @@ -709,7 +803,8 @@ function __construct() * ref="#/components/schemas/EvaluationRequest_evaluation_request_run", * ), *) - * + */ +/** * @OA\Schema( * schema="Run", * type="object", @@ -718,7 +813,8 @@ function __construct() * ref="#/components/schemas/Run_run_description", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_6_task_tag", * @OA\Property( @@ -727,7 +823,8 @@ function __construct() * description="ID of the tagged task", * ), *) - * + */ +/** * @OA\Schema( * schema="RunList", * type="object", @@ -736,7 +833,8 @@ function __construct() * ref="#/components/schemas/RunList_runs", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_17_data_delete", * @OA\Property( @@ -745,7 +843,8 @@ function __construct() * description="ID of the deleted run, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_input", * @OA\Property( @@ -756,6 +855,13 @@ function __construct() * property="cost_matrix", * type="array", * description="The cost matrix, indicating the costs for each type of misclassification", + * @OA\Items( + * type="array", + * @OA\Items( + * type="integer", + * format="int64" + * ) + * ) * ), * @OA\Property( * property="name", @@ -771,24 +877,31 @@ function __construct() * ref="#/components/schemas/Task_task_description_estimation_procedure", * ), *) - * + */ +/** * @OA\Schema( * schema="UserList", * type="object", * @OA\Property( * property="users", * ref="#/components/schemas/UserList_users", - * ), - *) + * ) * + *) + */ +/** * @OA\Schema( * schema="Study_study_runs", * @OA\Property( * property="run_id", * type="array", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="DataFeatures_data_features_feature", * @OA\Property( @@ -822,7 +935,8 @@ function __construct() * description="Whether this feature is a row identifier", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_21_upload_flow", * @OA\Property( @@ -831,7 +945,8 @@ function __construct() * description="ID of the evaluated run, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskType", * type="object", @@ -848,14 +963,23 @@ function __construct() * @OA\Property( * property="output", * type="array", + * @OA\Items( + * ref="#/components/schemas/TaskType_output" + * ) * ), * @OA\Property( * property="contributor", * type="array", + * @OA\Items( + * type="string" + * ) * ), * @OA\Property( * property="input", * type="array", + * @OA\Items( + * ref="#/components/schemas/TaskType_input" + * ) * ), * @OA\Property( * property="id", @@ -868,7 +992,8 @@ function __construct() * description="The name of the task type, e.g. Supervised Classification", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description_output_data_file", * @OA\Property( @@ -887,7 +1012,8 @@ function __construct() * description="The name of the uploaded file (e.g., description, predictions, model,...)", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskType_input", * @OA\Property( @@ -897,6 +1023,13 @@ function __construct() * @OA\Property( * property="cost_matrix", * type="array", + * @OA\Items( + * type="array", + * @OA\Items( + * type="integer", + * format="int64" + * ) + * ) * ), * @OA\Property( * property="name", @@ -912,19 +1045,27 @@ function __construct() * ref="#/components/schemas/Task_task_description_estimation_procedure", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description_output_data", * @OA\Property( * property="evaluation", * type="array", + * @OA\Items( + * ref="#/components/schemas/Run_run_description_output_data_evaluation" + * ) * ), * @OA\Property( * property="file", * type="array", + * @OA\Items( + * ref="#/components/schemas/Run_run_description_output_data_file" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_3_data_untag", * @OA\Property( @@ -933,7 +1074,8 @@ function __construct() * description="ID of the untagged dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_output", * @OA\Property( @@ -946,7 +1088,8 @@ function __construct() * ref="#/components/schemas/Task_task_description_predictions", * ), *) - * + */ +/** * @OA\Schema( * schema="DataQualityList", * type="object", @@ -955,7 +1098,8 @@ function __construct() * ref="#/components/schemas/DataQualityList_data_qualities_list", * ), *) - * + */ +/** * @OA\Schema( * schema="DataUnprocessed_data_unprocessed", * @OA\Property( @@ -963,7 +1107,8 @@ function __construct() * ref="#/components/schemas/DataUnprocessed_data_unprocessed_dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="Study", * type="object", @@ -972,15 +1117,20 @@ function __construct() * ref="#/components/schemas/Study_study", * ), *) - * + */ +/** * @OA\Schema( * schema="DataList_data", * @OA\Property( * property="dataset", * type="array", + * @OA\Items( + * ref="#/components/schemas/DataList_data_dataset" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="UserList_users_user", * @OA\Property( @@ -994,7 +1144,8 @@ function __construct() * description="The user ID", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskType_output", * @OA\Property( @@ -1007,7 +1158,8 @@ function __construct() * ref="#/components/schemas/TaskType_predictions", * ), *) - * + */ +/** * @OA\Schema( * schema="RunList_runs_run", * @OA\Property( @@ -1041,15 +1193,20 @@ function __construct() * description="The ID of the flow used in this run", * ), *) - * + */ +/** * @OA\Schema( * schema="DataFeatures_data_features", * @OA\Property( * property="feature", * type="array", + * @OA\Items( + * ref="#/components/schemas/DataFeatures_data_features_feature" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="DataQualities_data_qualities_quality", * @OA\Property( @@ -1063,7 +1220,8 @@ function __construct() * description="The value for this dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_12_flow_tag", * @OA\Property( @@ -1072,7 +1230,8 @@ function __construct() * description="ID of the tagged flow", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_8_flow_delete", * @OA\Property( @@ -1081,15 +1240,20 @@ function __construct() * description="ID of the deleted flow, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="DataQualities_data_qualities", * @OA\Property( * property="quality", * type="array", + * @OA\Items( + * ref="#/components/schemas/DataQualities_data_qualities_quality" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationRequest", * type="object", @@ -1098,7 +1262,8 @@ function __construct() * ref="#/components/schemas/EvaluationRequest_evaluation_request", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_18_upload_flow", * @OA\Property( @@ -1107,7 +1272,8 @@ function __construct() * description="ID of the uploaded run, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="SetupList", * type="object", @@ -1116,7 +1282,8 @@ function __construct() * ref="#/components/schemas/SetupList_setups", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskList", * type="object", @@ -1125,7 +1292,8 @@ function __construct() * ref="#/components/schemas/TaskList_task", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_4_task_delete", * @OA\Property( @@ -1134,7 +1302,8 @@ function __construct() * description="ID of the deleted task, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_data_set", * @OA\Property( @@ -1148,7 +1317,8 @@ function __construct() * description="The name of the target feature for this task", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationList", * type="object", @@ -1157,7 +1327,8 @@ function __construct() * ref="#/components/schemas/EvaluationList_evaluations", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description", * @OA\Property( @@ -1192,6 +1363,9 @@ function __construct() * @OA\Property( * property="tag", * type="array", + * @OA\Items( + * type="string" + * ), * description="Tags added by OpenML users. Includes study tags in the form `study_1`", * ), * @OA\Property( @@ -1221,9 +1395,13 @@ function __construct() * @OA\Property( * property="parameter_setting", * type="array", + * @OA\Items( + * ref="#/components/schemas/Run_run_description_parameter_setting" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="FlowList", * type="object", @@ -1232,7 +1410,8 @@ function __construct() * ref="#/components/schemas/FlowList_flows", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_19_run_tag", * @OA\Property( @@ -1241,7 +1420,8 @@ function __construct() * description="ID of the tagged run", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_8", * @OA\Property( @@ -1249,7 +1429,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_8_flow_delete", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_9", * @OA\Property( @@ -1257,15 +1438,20 @@ function __construct() * ref="#/components/schemas/inline_response_200_9_upload_flow", * ), *) - * + */ +/** * @OA\Schema( * schema="Study_study_data", * @OA\Property( * property="data_id", * type="array", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_1", * @OA\Property( @@ -1273,7 +1459,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_1_upload_data_set", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_2", * @OA\Property( @@ -1281,7 +1468,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_2_data_tag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_3", * @OA\Property( @@ -1289,7 +1477,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_3_data_untag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_4", * @OA\Property( @@ -1297,7 +1486,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_4_task_delete", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_5", * @OA\Property( @@ -1305,7 +1495,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_5_upload_task", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_6", * @OA\Property( @@ -1313,7 +1504,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_6_task_tag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_7", * @OA\Property( @@ -1321,7 +1513,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_7_task_untag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_data_delete", * @OA\Property( @@ -1330,12 +1523,16 @@ function __construct() * description="ID of the deleted dataset, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="RunTrace_trace", * @OA\Property( * property="trace_iteration", * type="array", + * @OA\Items( + * ref="#/components/schemas/RunTrace_trace_trace_iteration" + * ) * ), * @OA\Property( * property="run_id", @@ -1343,7 +1540,8 @@ function __construct() * description="run ID", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_7_task_untag", * @OA\Property( @@ -1352,7 +1550,8 @@ function __construct() * description="ID of the untagged task", * ), *) - * + */ +/** * @OA\Schema( * schema="Data", * type="object", @@ -1361,7 +1560,8 @@ function __construct() * ref="#/components/schemas/Data_data_set_description", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_evaluation_measures", * @OA\Property( @@ -1370,7 +1570,8 @@ function __construct() * description="The evaluation measure to optimize in this task", * ), *) - * + */ +/** * @OA\Schema( * schema="SetupList_setups_parameter", * @OA\Property( @@ -1414,7 +1615,8 @@ function __construct() * description="The parameter ID", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskList_task_input", * @OA\Property( @@ -1428,15 +1630,20 @@ function __construct() * description="The value of the input", * ), *) - * + */ +/** * @OA\Schema( * schema="StudyList_study_list", * @OA\Property( * property="study", * type="array", + * @OA\Items( + * ref="#/components/schemas/StudyList_study_list_study" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="Study_study_tag", * @OA\Property( @@ -1450,7 +1657,8 @@ function __construct() * description="The name of the study (e.g. study_1)", * ), *) - * + */ +/** * @OA\Schema( * schema="Flow", * type="object", @@ -1459,7 +1667,8 @@ function __construct() * ref="#/components/schemas/Flow_flow_description", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description_parameter_setting", * @OA\Property( @@ -1473,7 +1682,8 @@ function __construct() * description="The value of the parameter used", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationRequest_evaluation_request_run", * @OA\Property( @@ -1502,7 +1712,8 @@ function __construct() * description="ID of the run, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="Error", * type="object", @@ -1519,7 +1730,8 @@ function __construct() * type="string", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_23", * @OA\Property( @@ -1527,7 +1739,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_23_upload_flow", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_22", * @OA\Property( @@ -1535,7 +1748,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_21_upload_flow", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_21", * @OA\Property( @@ -1543,7 +1757,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_21_upload_flow", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_20", * @OA\Property( @@ -1551,7 +1766,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_20_run_untag", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_27", * @OA\Property( @@ -1559,7 +1775,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_26_study_attach", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_26", * @OA\Property( @@ -1567,7 +1784,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_26_study_attach", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_25", * @OA\Property( @@ -1575,7 +1793,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_25_upload_study", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_24", * @OA\Property( @@ -1583,7 +1802,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_24_study_delete", * ), *) - * + */ +/** * @OA\Schema( * schema="EstimationProcedureList_estimationprocedures_estimationprocedure", * @OA\Property( @@ -1622,15 +1842,20 @@ function __construct() * description="The estimation procedure ID", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskTypeList_task_types", * @OA\Property( * property="task_type", * type="array", + * @OA\Items( + * ref="#/components/schemas/TaskTypeList_task_types_task_type" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="TaskList_task_task", * @OA\Property( @@ -1666,17 +1891,27 @@ function __construct() * @OA\Property( * property="tag", * type="array", + * @OA\Items( + * type="string" + * ) * ), * @OA\Property( * property="input", * type="array", + * @OA\Items( + * ref="#/components/schemas/TaskList_task_input" + * ) * ), * @OA\Property( * property="quality", * type="array", + * @OA\Items( + * ref="#/components/schemas/TaskList_task_quality" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_20_run_untag", * @OA\Property( @@ -1685,7 +1920,8 @@ function __construct() * description="ID of the untagged run", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_11", * @OA\Property( @@ -1693,7 +1929,8 @@ function __construct() * ref="#/components/schemas/inline_response_200_11_flow_owned", * ), *) - * + */ +/** * @OA\Schema( * schema="StudyList", * type="object", @@ -1702,7 +1939,8 @@ function __construct() * ref="#/components/schemas/StudyList_study_list", * ), *) - * + */ +/** * @OA\Schema( * schema="Study_study", * @OA\Property( @@ -1756,15 +1994,20 @@ function __construct() * description="The ID of the study", * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_11_flow_owned", * @OA\Property( * property="id", * type="array", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_25_upload_study", * @OA\Property( @@ -1773,7 +2016,8 @@ function __construct() * description="ID of the uploaded study, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationList_evaluations_evaluation", * @OA\Property( @@ -1807,7 +2051,8 @@ function __construct() * description="The ID of the flow used by this run", * ), *) - * + */ +/** * @OA\Schema( * schema="EstimationProcedureList", * type="object", @@ -1816,23 +2061,32 @@ function __construct() * ref="#/components/schemas/EstimationProcedureList_estimationprocedures", * ), *) - * + */ +/** * @OA\Schema( * schema="DataQualityList_data_qualities_list", * @OA\Property( * property="quality", * type="array", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="Study_study_flows", * @OA\Property( * property="flow_id", * type="array", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="StudyList_study_list_study", * @OA\Property( @@ -1861,7 +2115,8 @@ function __construct() * description="The name of the study", * ), *) - * + */ +/** * @OA\Schema( * schema="Run_run_description_input_data", * @OA\Property( @@ -1869,7 +2124,8 @@ function __construct() * ref="#/components/schemas/Run_run_description_input_data_dataset", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskTypeList_task_types_task_type", * @OA\Property( @@ -1893,7 +2149,8 @@ function __construct() * description="The name of the task type", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationMeasureList_evaluation_measures", * @OA\Property( @@ -1901,12 +2158,16 @@ function __construct() * ref="#/components/schemas/EvaluationMeasureList_evaluation_measures_measures", * ), *) - * + */ +/** * @OA\Schema( * schema="TaskType_predictions", * @OA\Property( * property="feature", * type="array", + * @OA\Items( + * ref="#/components/schemas/Task_task_description_predictions_feature" + * ) * ), * @OA\Property( * property="format", @@ -1914,7 +2175,8 @@ function __construct() * description="The format of the predictions, e.g. ARFF", * ), *) - * + */ +/** * @OA\Schema( * schema="Flow_flow_description_parameter", * @OA\Property( @@ -1938,7 +2200,8 @@ function __construct() * description="A description of the parameter", * ), *) - * + */ +/** * @OA\Schema( * schema="RunTrace_trace_trace_iteration", * @OA\Property( @@ -1972,7 +2235,8 @@ function __construct() * description="The evaluation score of the setup", * ), *) - * + */ +/** * @OA\Schema( * schema="EvaluationMeasureList", * type="object", @@ -1981,23 +2245,32 @@ function __construct() * ref="#/components/schemas/EvaluationMeasureList_evaluation_measures", * ), *) - * + */ +/** * @OA\Schema( * schema="SetupList_setups", * @OA\Property( * property="setup", * type="array", + * @OA\Items( + * ref="#/components/schemas/SetupList_setups_setup" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="TaskList_task", * @OA\Property( * property="task", * type="array", + * @OA\Items( + * ref="#/components/schemas/TaskList_task_task" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="SetupList_setups_setup", * @OA\Property( @@ -2008,6 +2281,9 @@ function __construct() * @OA\Property( * property="parameter", * type="array", + * @OA\Items( + * ref="#/components/schemas/SetupList_setups_parameter" + * ) * ), * @OA\Property( * property="flow_id", @@ -2015,7 +2291,8 @@ function __construct() * description="The ID of the flow used by this run", * ), *) - * + */ +/** * @OA\Schema( * schema="Task_task_description_estimation_procedure_parameter", * @OA\Property( @@ -2029,23 +2306,32 @@ function __construct() * description="The value of the parameter", * ), *) - * + */ +/** * @OA\Schema( * schema="RunList_runs", * @OA\Property( * property="run", * type="array", + * @OA\Items( + * ref="#/components/schemas/RunList_runs_run" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="Study_study_tasks", * @OA\Property( * property="task_id", * type="array", + * @OA\Items( + * type="string" + * ) * ), *) - * + */ +/** * @OA\Schema( * schema="inline_response_200_1_upload_data_set", * @OA\Property( @@ -2054,7 +2340,8 @@ function __construct() * description="ID of the uploaded dataset, a positive integer", * ), *) - * + */ +/** * @OA\Schema( * schema="Setup_setup_parameters_parameter_setting", * @OA\Property( @@ -2083,7 +2370,8 @@ function __construct() * description="The short name of the hyperparameter", * ), *) - * + */ +/** * @OA\Schema( * schema="DataQualities", * type="object", From 26d0501d0df2bb796e3ab1863c8116b04a91119a Mon Sep 17 00:00:00 2001 From: mwever Date: Wed, 8 Jul 2020 18:31:27 +0200 Subject: [PATCH 07/15] Fixed issue with tasktype API description. --- openml_OS/models/api/v1/Api_tasktype.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openml_OS/models/api/v1/Api_tasktype.php b/openml_OS/models/api/v1/Api_tasktype.php index 9ead049d9..7352bec8d 100644 --- a/openml_OS/models/api/v1/Api_tasktype.php +++ b/openml_OS/models/api/v1/Api_tasktype.php @@ -77,15 +77,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { * summary="Get task type description", * description="Returns information about a task type. The information includes a description, the given inputs and the expected outputs.", * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), - * @OA\Parameter( * name="id", * in="path", * @OA\Schema( @@ -94,6 +85,15 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="ID of the task.", * required=true, * ), + * @OA\Parameter( + * name="api_key", + * in="query", + * @OA\Schema( + * type="string" + * ), + * description="API key to authenticate the user", + * required=false, + * ), * @OA\Response( * response=200, * description="A task type description", From 88204b4331e878894574392919ec14a09d94df64 Mon Sep 17 00:00:00 2001 From: mwever Date: Thu, 9 Jul 2020 11:16:22 +0200 Subject: [PATCH 08/15] Added bash/bat scripts for linux and windows respectively to generate openapi specifications from inline php annotations. --- .gitignore | 2 + openapi/composer.json | 14 ++ openapi/composer.lock | 377 ++++++++++++++++++++++++++++++++++ openapi/composer.phar | Bin 0 -> 1811606 bytes openapi/generate_json_api.bat | 1 + openapi/generate_json_api.sh | 3 + openapi/generate_yaml_api.bat | 1 + openapi/generate_yaml_api.sh | 3 + 8 files changed, 401 insertions(+) create mode 100644 openapi/composer.json create mode 100644 openapi/composer.lock create mode 100644 openapi/composer.phar create mode 100644 openapi/generate_json_api.bat create mode 100644 openapi/generate_json_api.sh create mode 100644 openapi/generate_yaml_api.bat create mode 100644 openapi/generate_yaml_api.sh diff --git a/.gitignore b/.gitignore index 36bf2307e..728ccc53f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ data/* openml_OS/config/BASE_CONFIG.php openml_OS/third_party/OpenML/Java/old-evaluate.jar docs/site/ +openml_OS/vendor/ +openapi/vendor/ \ No newline at end of file diff --git a/openapi/composer.json b/openapi/composer.json new file mode 100644 index 000000000..2250e5b9a --- /dev/null +++ b/openapi/composer.json @@ -0,0 +1,14 @@ +{ + "description": "OpenML OpenAPI Specification", + "name": "openml/openapi", + "type": "project", + "homepage": "https://openml.org", + "license": "", + "support": { + "source": "https://github.com/openml/OpenML" + }, + "require": { + "php": ">=5.2.4", + "zircote/swagger-php": "^3.0" + } +} diff --git a/openapi/composer.lock b/openapi/composer.lock new file mode 100644 index 000000000..60b54f452 --- /dev/null +++ b/openapi/composer.lock @@ -0,0 +1,377 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "57a385db00a99dcd2f118bc72879de3b", + "packages": [ + { + "name": "doctrine/annotations", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc", + "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^7.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2019-10-01T18:55:10+00:00" + }, + { + "name": "doctrine/lexer", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "^4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "time": "2019-06-08T11:03:04+00:00" + }, + { + "name": "symfony/finder", + "version": "v4.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "5e575faa95548d0586f6bedaeabec259714e44d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/5e575faa95548d0586f6bedaeabec259714e44d1", + "reference": "5e575faa95548d0586f6bedaeabec259714e44d1", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2019-09-16T11:29:48+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-08-06T08:03:45+00:00" + }, + { + "name": "symfony/yaml", + "version": "v4.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/41e16350a2a1c7383c4735aa2f9fce74cf3d1178", + "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2019-09-11T15:41:19+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "f10ab7f81d89dba97653a980cc90cf4b7b73f543" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/f10ab7f81d89dba97653a980cc90cf4b7b73f543", + "reference": "f10ab7f81d89dba97653a980cc90cf4b7b73f543", + "shasum": "" + }, + "require": { + "doctrine/annotations": "*", + "php": ">=7.0", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" + }, + "require-dev": { + "phpunit/phpunit": ">=6.3", + "squizlabs/php_codesniffer": ">=3.3", + "zendframework/zend-form": "<2.8" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "autoload": { + "psr-4": { + "OpenApi\\": "src" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Robert Allen", + "email": "zircote@gmail.com", + "homepage": "http://www.zircote.com" + }, + { + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "http://bfanger.nl" + } + ], + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", + "keywords": [ + "api", + "json", + "rest", + "service discovery" + ], + "time": "2018-11-16T15:04:29+00:00" + } + ], + "packages-dev": [ ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.2.4" + }, + "platform-dev": [] +} diff --git a/openapi/composer.phar b/openapi/composer.phar new file mode 100644 index 0000000000000000000000000000000000000000..89920d3980b4ac429ef7455d6df9c4fb7bc6c3be GIT binary patch literal 1811606 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%0All31cGatv8K>J2O27XH%q-h>Jmz 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 nx-IEVhyDS!ewZo#ooz#%5TdZBtJ`znNGl(PC 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

VOmuek?i2;rMi|)99fxNGzr&r~ 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

Z$X69Mg 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%0Dqyawpy3=Ug;b}B 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

M9^ws8tc9;WSy6yv87#h^TAGverwSh3dX@Tgl^ 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 Date: Fri, 10 Jul 2020 09:45:53 +0200 Subject: [PATCH 09/15] Added server specs and info description to the annotations. --- openml_OS/controllers/Api.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openml_OS/controllers/Api.php b/openml_OS/controllers/Api.php index a81b83ad9..198006710 100644 --- a/openml_OS/controllers/Api.php +++ b/openml_OS/controllers/Api.php @@ -2,8 +2,15 @@ require_once('Api_new.php'); /** - * @OA\Info(title="OpenML API", version="1.0.0") - * REST API for sharing, organizing and reusing machine learning datasets, code, and experiments. Follows a predictive URL scheme from endpoint https://www.openml.org/api/v1/json (or /xml). You need to add your `api_key` to every call (see your account settings), or simply log in. See https://www.openml.org/api_data_docs for the file server API. + * @OA\Info(title="OpenML API", version="1.0.0", description="REST API for sharing, organizing and reusing machine learning datasets, code, and experiments. Follows a predictive URL scheme from endpoint https://www.openml.org/api/v1/json (or /xml). You need to add your `api_key` to every call (see your account settings), or simply log in. See https://www.openml.org/api_data_docs for the file server API." + * ) + */ + + /** + * @OA\Server( + * url="https://openml.org/api/v1/json", + * description="The base path of the current OpenML's API" + * ) */ class Api extends Api_new { From cf9d2856df0e8b0ea3b722cf46cea70b00b9cfe1 Mon Sep 17 00:00:00 2001 From: mwever Date: Fri, 10 Jul 2020 10:15:02 +0200 Subject: [PATCH 10/15] Removed API key parameters from GET methods, replaced overall description and added servers for base paths. --- openml_OS/controllers/Api.php | 8 ++- openml_OS/models/api/v1/Api_data.php | 54 ------------------- .../models/api/v1/Api_estimationprocedure.php | 9 ---- openml_OS/models/api/v1/Api_evaluation.php | 18 ------- .../models/api/v1/Api_evaluationmeasure.php | 9 ---- openml_OS/models/api/v1/Api_flow.php | 27 ---------- openml_OS/models/api/v1/Api_run.php | 36 ------------- openml_OS/models/api/v1/Api_setup.php | 18 ------- openml_OS/models/api/v1/Api_study.php | 18 ------- openml_OS/models/api/v1/Api_task.php | 18 ------- openml_OS/models/api/v1/Api_tasktype.php | 18 ------- openml_OS/models/api/v1/Api_user.php | 9 ---- 12 files changed, 6 insertions(+), 236 deletions(-) diff --git a/openml_OS/controllers/Api.php b/openml_OS/controllers/Api.php index 198006710..4600a4276 100644 --- a/openml_OS/controllers/Api.php +++ b/openml_OS/controllers/Api.php @@ -2,14 +2,18 @@ require_once('Api_new.php'); /** - * @OA\Info(title="OpenML API", version="1.0.0", description="REST API for sharing, organizing and reusing machine learning datasets, code, and experiments. Follows a predictive URL scheme from endpoint https://www.openml.org/api/v1/json (or /xml). You need to add your `api_key` to every call (see your account settings), or simply log in. See https://www.openml.org/api_data_docs for the file server API." + * @OA\Info(title="OpenML API", version="1.0.0", description="REST API for sharing, organizing and reusing machine learning datasets, code, and experiments. Follows a predictive URL scheme from endpoint https://www.openml.org/api/v1/json (or /xml). You need to add your api_key for every call except GET calls. The API key can be found in your profile on openml.org." * ) */ /** * @OA\Server( * url="https://openml.org/api/v1/json", - * description="The base path of the current OpenML's API" + * description="The current OpenML API" + * ) + * @OA\Server( + * url="https://test.openml.org/api/v1/json", + * description="The test OpenML API" * ) */ class Api extends Api_new diff --git a/openml_OS/models/api/v1/Api_data.php b/openml_OS/models/api/v1/Api_data.php index 769664ce1..fb94eb7cf 100644 --- a/openml_OS/models/api/v1/Api_data.php +++ b/openml_OS/models/api/v1/Api_data.php @@ -293,15 +293,6 @@ private function data_tag_untag($data_id,$tag, $do_untag) { ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of datasets with the given task", @@ -480,15 +471,6 @@ private function data_list($segs) { * description="Id of the dataset.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="Api key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A dataset description", @@ -1066,15 +1048,6 @@ private function status_update($data_id, $status) { * description="Id of the dataset.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="Api key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="All the features of the dataset", @@ -1407,15 +1380,6 @@ private function data_features_upload() { * tags={"data"}, * summary="List all data qualities", * description="Returns a list of all data qualities in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of data qualities", @@ -1490,15 +1454,6 @@ private function feature_qualities_list() { * description="Id of the dataset.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="Api key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="All the qualities of the dataset", @@ -1851,15 +1806,6 @@ private function data_qualities_upload() { * description="When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of unprocessed datasets", diff --git a/openml_OS/models/api/v1/Api_estimationprocedure.php b/openml_OS/models/api/v1/Api_estimationprocedure.php index 9420cf44b..beccb51ba 100644 --- a/openml_OS/models/api/v1/Api_estimationprocedure.php +++ b/openml_OS/models/api/v1/Api_estimationprocedure.php @@ -48,15 +48,6 @@ private function estimationprocedure($id) { * tags={"estimationprocedure"}, * summary="List all estimation procedures", * description="Returns an array with all model performance estimation procedures in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of estimation procedures", diff --git a/openml_OS/models/api/v1/Api_evaluation.php b/openml_OS/models/api/v1/Api_evaluation.php index e489cace6..4a5dc537a 100644 --- a/openml_OS/models/api/v1/Api_evaluation.php +++ b/openml_OS/models/api/v1/Api_evaluation.php @@ -65,15 +65,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="When there are multiple runs still to evaluate, this defines which one to return. Options are 'normal' - the oldest run, 'reverse' - the newest run, or 'random' - a random run.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of evaluations descriptions", @@ -171,15 +162,6 @@ private function evaluation_request($evaluation_engine_id, $order, $num_request ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of evaluations descriptions", diff --git a/openml_OS/models/api/v1/Api_evaluationmeasure.php b/openml_OS/models/api/v1/Api_evaluationmeasure.php index 4ad961f65..e3c5aadfe 100644 --- a/openml_OS/models/api/v1/Api_evaluationmeasure.php +++ b/openml_OS/models/api/v1/Api_evaluationmeasure.php @@ -31,15 +31,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { * tags={"evaluationmeasure"}, * summary="List all evaluation measures", * description="Returns an array with all model evaluation measures in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of evaluation measures", diff --git a/openml_OS/models/api/v1/Api_flow.php b/openml_OS/models/api/v1/Api_flow.php index 8302a8dae..142f80e8a 100644 --- a/openml_OS/models/api/v1/Api_flow.php +++ b/openml_OS/models/api/v1/Api_flow.php @@ -102,15 +102,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of flows", @@ -365,15 +356,6 @@ private function flow_tag_untag($flow_id, $tag, $do_untag) { * description="The external version of the flow", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of flows", @@ -432,15 +414,6 @@ private function flow_exists($name, $external_version) { * description="ID of the flow.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A flow description", diff --git a/openml_OS/models/api/v1/Api_run.php b/openml_OS/models/api/v1/Api_run.php index ad3ae30e7..d60e781c5 100644 --- a/openml_OS/models/api/v1/Api_run.php +++ b/openml_OS/models/api/v1/Api_run.php @@ -254,15 +254,6 @@ private function run_tag_untag($run_id, $tag, $do_untag) { ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of runs descriptions", @@ -396,15 +387,6 @@ private function run_list($segs, $user_id) { * description="ID of the run.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A run description", @@ -637,15 +619,6 @@ private function run_delete($run_id) { * description="Run ID.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="Id of the evaluated run", @@ -1086,15 +1059,6 @@ private function run_upload() { * description="ID of the run.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A run trace description", diff --git a/openml_OS/models/api/v1/Api_setup.php b/openml_OS/models/api/v1/Api_setup.php index 05222726a..fff2dd4bd 100644 --- a/openml_OS/models/api/v1/Api_setup.php +++ b/openml_OS/models/api/v1/Api_setup.php @@ -227,15 +227,6 @@ private function setup_tag_untag($setup_id, $tag, $do_untag) { * description="ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A setup description", @@ -325,15 +316,6 @@ private function setup($setup_id) { ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of setup descriptions", diff --git a/openml_OS/models/api/v1/Api_study.php b/openml_OS/models/api/v1/Api_study.php index 6b2d3d333..d4edd56c3 100644 --- a/openml_OS/models/api/v1/Api_study.php +++ b/openml_OS/models/api/v1/Api_study.php @@ -552,15 +552,6 @@ private function study_delete($study_id) { ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of studies", @@ -666,15 +657,6 @@ private function study_list($segs) { * description="ID or alias of the study.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="Api key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A study description", diff --git a/openml_OS/models/api/v1/Api_task.php b/openml_OS/models/api/v1/Api_task.php index a20f66822..cd95f2565 100644 --- a/openml_OS/models/api/v1/Api_task.php +++ b/openml_OS/models/api/v1/Api_task.php @@ -221,15 +221,6 @@ private function task_tag_untag($task_id, $tag, $do_untag) { ", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of tasks with the given tag", @@ -423,15 +414,6 @@ private function task_list($segs, $user_id) { * description="ID of the task.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="Api key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A task description", diff --git a/openml_OS/models/api/v1/Api_tasktype.php b/openml_OS/models/api/v1/Api_tasktype.php index 7352bec8d..2f6552a46 100644 --- a/openml_OS/models/api/v1/Api_tasktype.php +++ b/openml_OS/models/api/v1/Api_tasktype.php @@ -22,15 +22,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { * tags={"tasktype"}, * summary="List all task types", * description="Returns an array with all task types in the system.", - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response="default", * description="Unexpected error", @@ -85,15 +76,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { * description="ID of the task.", * required=true, * ), - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A task type description", diff --git a/openml_OS/models/api/v1/Api_user.php b/openml_OS/models/api/v1/Api_user.php index 0847865b0..71b946bab 100644 --- a/openml_OS/models/api/v1/Api_user.php +++ b/openml_OS/models/api/v1/Api_user.php @@ -89,15 +89,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { * tags={"user"}, * summary="List all users by user id", * description="Returns an array with all user ids and names.", - * @OA\Parameter( - * name="api_key", - * in="query", - * @OA\Schema( - * type="string" - * ), - * description="API key to authenticate the user", - * required=false, - * ), * @OA\Response( * response=200, * description="A list of users", From 65b6501e6e09f01613c8146d31487ba51fe0cf78 Mon Sep 17 00:00:00 2001 From: mwever Date: Fri, 10 Jul 2020 10:45:27 +0200 Subject: [PATCH 11/15] Repaired the code which got screwed up during merging develop into this feature branch. --- openml_OS/models/api/v1/Api_evaluation.php | 66 +++++++++++++--------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/openml_OS/models/api/v1/Api_evaluation.php b/openml_OS/models/api/v1/Api_evaluation.php index 4a5dc537a..84541a6d0 100644 --- a/openml_OS/models/api/v1/Api_evaluation.php +++ b/openml_OS/models/api/v1/Api_evaluation.php @@ -27,7 +27,6 @@ function bootstrap($format, $segments, $request_type, $user_id) { } $order_values = array('random', 'reverse', 'normal'); - if (count($segments) >= 4 && $segments[0] == 'request' && is_numeric($segments[1]) && in_array($segments[2], $order_values) && is_numeric($segments[3])) { array_shift($segments); // removes 'request' $eval_id = array_shift($segments); @@ -179,7 +178,7 @@ private function evaluation_request($evaluation_engine_id, $order, $num_request * ), *) */ - private function evaluation_list($segs, $user_id) { + private function evaluation_list($segs, $user_id, $show_params) { $result_limit = 10000; $legal_filters = array('task', 'setup', 'flow', 'uploader', 'run', 'tag', 'limit', 'offset', 'function', 'per_fold', 'sort_order', 'study'); list($query_string, $illegal_filters) = $this->parse_filters($segs, $legal_filters); @@ -187,7 +186,7 @@ private function evaluation_list($segs, $user_id) { $this->returnError(544, $this->version, $this->openmlGeneralErrorCode, 'Legal filter operators: ' . implode(',', $legal_filters) .'. Found illegal filter(s): ' . implode(', ', $illegal_filters)); return; } - + $illegal_filter_inputs = $this->check_filter_inputs($query_string, $legal_filters, array('tag', 'function', 'per_fold', 'sort_order')); if (count($illegal_filter_inputs) > 0) { $this->returnError(541, $this->version, $this->openmlGeneralErrorCode, 'Filters with illegal values: ' . implode(',', $illegal_filter_inputs)); @@ -211,7 +210,7 @@ private function evaluation_list($segs, $user_id) { return; } $per_fold = ($per_fold === 'true') ? true : false; // cast to boolean. we only accept lowercase entrees - + if ($offset && !$limit) { $this->returnError(545, $this->version); return; @@ -224,7 +223,7 @@ private function evaluation_list($segs, $user_id) { $this->returnError(549, $this->version); return; } - + if ($study_id) { $study = $this->Study->getById($study_id); if ($study === false || $study->legacy != 'n' || $study->main_entity_type != 'run') { @@ -251,7 +250,7 @@ private function evaluation_list($segs, $user_id) { $where_limit = ' LIMIT ' . $offset . ',' . $limit; } $where_task_closed = ' AND (`t`.`embargo_end_date` is NULL OR `t`.`embargo_end_date` < NOW() OR `r`.`uploader` = '.$user_id.')'; - + $where_runs = $where_task . $where_setup . $where_uploader . $where_impl . $where_run . $where_tag . $where_study . $where_task_closed; $where_total = $where_runs . $where_function; @@ -261,17 +260,17 @@ private function evaluation_list($segs, $user_id) { //shortcuts if (!$implementation_id) { $sql_test = - 'SELECT count(distinct r.rid) as count ' . - 'FROM run r, task t '. - 'WHERE r.task_id = t.task_id ' . - $where_runs; + 'SELECT count(distinct r.rid) as count ' . + 'FROM run r, task t '. + 'WHERE r.task_id = t.task_id ' . + $where_runs; $count = $this->Evaluation->query($sql_test)[0]->count; } else { $sql_test = - 'SELECT count(distinct r.rid) as count ' . - 'FROM run r, task t, algorithm_setup s ' . - 'WHERE r.setup = s.sid AND r.task_id = t.task_id ' . - $where_runs; + 'SELECT count(distinct r.rid) as count ' . + 'FROM run r, task t, algorithm_setup s ' . + 'WHERE r.setup = s.sid AND r.task_id = t.task_id ' . + $where_runs; $count = $this->Evaluation->query($sql_test)[0]->count; } if ($count > $result_limit) { @@ -285,12 +284,12 @@ private function evaluation_list($segs, $user_id) { } } } - + $order_by = null; if ($sort_order) { $order_by = 'ORDER BY `e`.`value` ' . $sort_order; } - + if ($per_fold) { $eval_table = 'evaluation_fold'; $columns = 'NULL as value, NULL as array_data, CONCAT("[", GROUP_CONCAT(e.value), "]") AS `values`'; @@ -307,17 +306,17 @@ private function evaluation_list($segs, $user_id) { // TODO: remove dependency on task_inputs and dataset table // TODO (2): transform into subquery where all columns except evaluation_fold are obtained in subquery (along with limit requirements, as MYSQL query optimizer does not seem to understand this query has an upper limit to the number of obtained runs that need to be inspected) $sql = - 'SELECT r.rid, r.task_id, r.start_time, r.uploader, s.implementation_id, s.sid, f.name AS `function`, i.fullName, d.did, d.name, e.evaluation_engine_id, ' . $columns . ' ' . - 'FROM run r, ' . $eval_table . ' e, algorithm_setup s, implementation i, dataset d, task t, task_inputs ti, math_function f ' . - 'WHERE r.setup = s.sid ' . - 'AND e.source = r.rid ' . - 'AND e.function_id = f.id ' . - 'AND s.implementation_id = i.id ' . - 'AND r.task_id = t.task_id ' . - 'AND r.task_id = ti.task_id ' . - 'AND ti.input = "source_data" ' . - 'AND ti.value = d.did ' . $where_total; - + 'SELECT r.rid, r.task_id, r.start_time, r.uploader, s.implementation_id, s.sid, f.name AS `function`, i.fullName, d.did, d.name, e.evaluation_engine_id, ' . $columns . ' ' . + 'FROM run r, ' . $eval_table . ' e, algorithm_setup s, implementation i, dataset d, task t, task_inputs ti, math_function f ' . + 'WHERE r.setup = s.sid ' . + 'AND e.source = r.rid ' . + 'AND e.function_id = f.id ' . + 'AND s.implementation_id = i.id ' . + 'AND r.task_id = t.task_id ' . + 'AND r.task_id = ti.task_id ' . + 'AND ti.input = "source_data" ' . + 'AND ti.value = d.did ' . $where_total; + if ($group_by) { $sql .= $group_by; } @@ -327,7 +326,7 @@ private function evaluation_list($segs, $user_id) { if ($where_limit) { $sql .= $where_limit; } - + $res = $this->Evaluation->query($sql); if ($res == false) { @@ -335,6 +334,17 @@ private function evaluation_list($segs, $user_id) { return; } + if ($show_params) { + # 2 stage query .. unfortunately. Can break when too much results. let's take the damage for now + $setup_ids = array(); + foreach ($res as $r) { + $setup_ids[] = $r->sid; + } + $params = $this->Algorithm_setup->setup_ids_to_parameter_values(array_unique($setup_ids)); + for ($i = 0; $i < count($res); ++$i) { + $res[$i]->parameters = $params[$res[$i]->sid]; + } + } $this->xmlContents('evaluations', $this->version, array('evaluations' => $res)); } } From bf75dae39637924f2a582dbbc2a3040000c799e4 Mon Sep 17 00:00:00 2001 From: mwever Date: Fri, 10 Jul 2020 15:34:43 +0200 Subject: [PATCH 12/15] Added python script for filtering out unused OpenAPI schemas. Removed unreferenced OpenAPI schemas. --- openapi/unusedSchemas.py | 55 +++++++ openml_OS/controllers/Api.php | 264 ---------------------------------- 2 files changed, 55 insertions(+), 264 deletions(-) create mode 100644 openapi/unusedSchemas.py diff --git a/openapi/unusedSchemas.py b/openapi/unusedSchemas.py new file mode 100644 index 000000000..5cf8cc3e1 --- /dev/null +++ b/openapi/unusedSchemas.py @@ -0,0 +1,55 @@ +import json + +with open('swagger.json',) as f: + data = json.load(f) + +schemasAvailable = list() +schemasReferenced = set() +for k,v in data.items(): + if k=="components": + for k2,v2 in v["schemas"].items(): + schemasAvailable.append(k2) + elif k=="paths": + for k2,v2 in v.items(): + for k3,v3 in v2.items(): + if "parameters" in v3: + for x in v3["parameters"]: + if "$ref" in x["schema"]: + schemasReferenced.add(x["schema"]["$ref"][21:]) + for k4,v4 in v3["responses"].items(): + cur = v4["content"]["application/json"]["schema"] + if "$ref" in cur: + schemasReferenced.add(cur["$ref"][21:]) + if "properties" in cur: + for x,y in cur["properties"].items(): + schemasReferenced.add(y["$ref"][21:]) + +schemasIndirectlyReferenced = set() +schemasIndirectlyReferenced.update(schemasReferenced) + +schemasReferencedQueue = list(schemasReferenced) + +while len(schemasReferencedQueue) > 0: + ele = schemasReferencedQueue[0] + schemasReferencedQueue.remove(ele) + schemasIndirectlyReferenced.add(ele) + for k,v in data["components"]["schemas"][ele]["properties"].items(): + if "$ref" in v: + schemaName = v["$ref"][21:] + if not (schemaName in schemasIndirectlyReferenced): + schemasReferencedQueue.append(schemaName) + if "items" in v and "$ref" in v["items"]: + schemaName = v["items"]["$ref"][21:] + if not (schemaName in schemasIndirectlyReferenced): + schemasReferencedQueue.append(schemaName) + +print("Schemas listed: ", len(schemasAvailable)) +print("Schemas directly referenced: ", len(schemasReferenced)) +print("Schemas (in)directly referenced in total: ", len(schemasIndirectlyReferenced)) + +for e in schemasIndirectlyReferenced: + schemasAvailable.remove(e) + +print("Found ", len(schemasAvailable), " unused schemas: ") +for s in schemasAvailable: + print(s) \ No newline at end of file diff --git a/openml_OS/controllers/Api.php b/openml_OS/controllers/Api.php index 4600a4276..12a529d4e 100644 --- a/openml_OS/controllers/Api.php +++ b/openml_OS/controllers/Api.php @@ -37,15 +37,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200", - * @OA\Property( - * property="data_delete", - * ref="#/components/schemas/inline_response_200_data_delete", - * ), - *) - */ /** * @OA\Schema( * schema="EvaluationMeasureList_evaluation_measures_measures", @@ -715,33 +706,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_12", - * @OA\Property( - * property="flow_tag", - * ref="#/components/schemas/inline_response_200_12_flow_tag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_13", - * @OA\Property( - * property="flow_untag", - * ref="#/components/schemas/inline_response_200_13_flow_untag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_10", - * @OA\Property( - * property="flow_exists", - * ref="#/components/schemas/inline_response_200_10_flow_exists", - * ), - *) - */ /** * @OA\Schema( * schema="inline_response_200_23_upload_flow", @@ -752,60 +716,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_16", - * @OA\Property( - * property="flow_untag", - * ref="#/components/schemas/inline_response_200_16_flow_untag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_17", - * @OA\Property( - * property="data_delete", - * ref="#/components/schemas/inline_response_200_17_data_delete", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_14", - * @OA\Property( - * property="study_delete", - * ref="#/components/schemas/inline_response_200_14_study_delete", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_15", - * @OA\Property( - * property="flow_tag", - * ref="#/components/schemas/inline_response_200_15_flow_tag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_18", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_18_upload_flow", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_19", - * @OA\Property( - * property="run_tag", - * ref="#/components/schemas/inline_response_200_19_run_tag", - * ), - *) - */ /** * @OA\Schema( * schema="EvaluationRequest_evaluation_request", @@ -1432,24 +1342,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_8", - * @OA\Property( - * property="flow_delete", - * ref="#/components/schemas/inline_response_200_8_flow_delete", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_9", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_9_upload_flow", - * ), - *) - */ /** * @OA\Schema( * schema="Study_study_data", @@ -1462,69 +1354,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_1", - * @OA\Property( - * property="upload_data_set", - * ref="#/components/schemas/inline_response_200_1_upload_data_set", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_2", - * @OA\Property( - * property="data_tag", - * ref="#/components/schemas/inline_response_200_2_data_tag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_3", - * @OA\Property( - * property="data_untag", - * ref="#/components/schemas/inline_response_200_3_data_untag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_4", - * @OA\Property( - * property="task_delete", - * ref="#/components/schemas/inline_response_200_4_task_delete", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_5", - * @OA\Property( - * property="upload_task", - * ref="#/components/schemas/inline_response_200_5_upload_task", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_6", - * @OA\Property( - * property="task_tag", - * ref="#/components/schemas/inline_response_200_6_task_tag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_7", - * @OA\Property( - * property="task_untag", - * ref="#/components/schemas/inline_response_200_7_task_untag", - * ), - *) - */ /** * @OA\Schema( * schema="inline_response_200_data_delete", @@ -1742,78 +1571,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_23", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_23_upload_flow", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_22", - * @OA\Property( - * property="run_reset", - * ref="#/components/schemas/inline_response_200_21_upload_flow", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_21", - * @OA\Property( - * property="upload_flow", - * ref="#/components/schemas/inline_response_200_21_upload_flow", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_20", - * @OA\Property( - * property="run_untag", - * ref="#/components/schemas/inline_response_200_20_run_untag", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_27", - * @OA\Property( - * property="upload_study", - * ref="#/components/schemas/inline_response_200_26_study_attach", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_26", - * @OA\Property( - * property="study_attach", - * ref="#/components/schemas/inline_response_200_26_study_attach", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_25", - * @OA\Property( - * property="upload_study", - * ref="#/components/schemas/inline_response_200_25_upload_study", - * ), - *) - */ -/** - * @OA\Schema( - * schema="inline_response_200_24", - * @OA\Property( - * property="study_delete", - * ref="#/components/schemas/inline_response_200_24_study_delete", - * ), - *) - */ /** * @OA\Schema( * schema="EstimationProcedureList_estimationprocedures_estimationprocedure", @@ -1932,15 +1689,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_11", - * @OA\Property( - * property="flow_owned", - * ref="#/components/schemas/inline_response_200_11_flow_owned", - * ), - *) - */ /** * @OA\Schema( * schema="StudyList", @@ -2006,18 +1754,6 @@ function __construct() * ), *) */ -/** - * @OA\Schema( - * schema="inline_response_200_11_flow_owned", - * @OA\Property( - * property="id", - * type="array", - * @OA\Items( - * type="string" - * ) - * ), - *) - */ /** * @OA\Schema( * schema="inline_response_200_25_upload_study", From ecfb5aaead09566f2075cec5a4cb571fda1cd7ee Mon Sep 17 00:00:00 2001 From: mwever Date: Wed, 15 Jul 2020 09:40:31 +0200 Subject: [PATCH 13/15] Added swagger json and yaml file. --- openapi/swagger.json | 5647 ++++++++++++++++++++++++++++++++++++++++++ openapi/swagger.yaml | 3338 +++++++++++++++++++++++++ 2 files changed, 8985 insertions(+) create mode 100644 openapi/swagger.json create mode 100644 openapi/swagger.yaml diff --git a/openapi/swagger.json b/openapi/swagger.json new file mode 100644 index 000000000..bfb522c48 --- /dev/null +++ b/openapi/swagger.json @@ -0,0 +1,5647 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "OpenML API", + "description": "REST API for sharing, organizing and reusing machine learning datasets, code, and experiments. Follows a predictive URL scheme from endpoint https://www.openml.org/api/v1/json (or /xml). You need to add your api_key for every call except GET calls. The API key can be found in your profile on openml.org.", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://openml.org/api/v1/json", + "description": "The current OpenML API" + }, + { + "url": "https://test.openml.org/api/v1/json", + "description": "The test OpenML API" + } + ], + "paths": { + "/data/tag": { + "post": { + "tags": [ + "data" + ], + "summary": "Tag a dataset", + "description": "Tags a dataset.", + "operationId": "Api_data::data_tag", + "parameters": [ + { + "name": "data_id", + "in": "query", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the tagged dataset", + "content": { + "application/json": { + "schema": { + "properties": { + "data_tag": { + "$ref": "#/components/schemas/inline_response_200_2_data_tag" + } + }, + "type": "object" + }, + "example": { + "data_tag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\\n474 - Internal error tagging the entity. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/untag": { + "post": { + "tags": [ + "data" + ], + "summary": "Untag a dataset", + "description": "Untags a dataset.", + "operationId": "Api_data::data_untag", + "parameters": [ + { + "name": "data_id", + "in": "query", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The ID of the untagged dataset", + "content": { + "application/json": { + "schema": { + "properties": { + "data_untag": { + "$ref": "#/components/schemas/inline_response_200_3_data_untag" + } + }, + "type": "object" + }, + "example": { + "data_untag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\\n479 - Internal error removing the tag. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/list/{filters}": { + "get": { + "tags": [ + "data" + ], + "summary": "List and filter datasets", + "description": "List datasets, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/data/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all datasets that match the constraints.", + "operationId": "Api_data::data_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n /status/{status} - returns only datasets with a given status, either 'active', 'deactivated', or 'in_preparation'.\r\n /tag/{tag} - returns only datasets tagged with the given tag.\r\n /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, data_version, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'.\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of datasets with the given task", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataList" + }, + "example": { + "data": { + "dataset": [ + { + "did": "1", + "name": "anneal", + "status": "active", + "format": "ARFF", + "quality": [ + { + "name": "MajorityClassSize", + "value": "684" + }, + { + "name": "MaxNominalAttDistinctValues", + "value": "10.0" + }, + { + "name": "MinorityClassSize", + "value": "0" + }, + { + "name": "NumBinaryAtts", + "value": "14.0" + }, + { + "name": "NumberOfClasses", + "value": "6" + }, + { + "name": "NumberOfFeatures", + "value": "39" + }, + { + "name": "NumberOfInstances", + "value": "898" + }, + { + "name": "NumberOfInstancesWithMissingValues", + "value": "0" + }, + { + "name": "NumberOfMissingValues", + "value": "0" + }, + { + "name": "NumberOfNumericFeatures", + "value": "6" + }, + { + "name": "NumberOfSymbolicFeatures", + "value": "32" + } + ] + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n370 - Illegal filter specified.\\n371 - Filter values/ranges not properly specified.\\n372 - No results. There where no matches for the given constraints.\\n373 - Can not specify an offset without a limit.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/{id}": { + "get": { + "tags": [ + "data" + ], + "summary": "Get dataset description", + "description": "Returns information about a dataset. The information includes the name, information about the creator, URL to download it and more.", + "operationId": "Api_data::data", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A dataset description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Data" + }, + "example": { + "data_set_description": { + "id": "1", + "name": "anneal", + "version": "2", + "description": "...", + "format": "ARFF", + "upload_date": "2014-04-06 23:19:20", + "licence": "Public", + "url": "https://www.openml.org/data/download/1/dataset_1_anneal.arff", + "file_id": "1", + "default_target_attribute": "class", + "version_label": "2", + "tag": [ + "study_1", + "uci" + ], + "visibility": "public", + "original_data_url": "https://www.openml.org/d/2", + "status": "active", + "md5_checksum": "d01f6ccd68c88b749b20bbe897de3713" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned\\n110 - Please provide data_id.\\n111 - Unknown dataset. Data set description with data_id was not found in the database.\\n112 - No access granted. This dataset is not shared with you.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "tags": [ + "data" + ], + "summary": "Delete dataset", + "description": "Deletes a dataset. Upon success, it returns the ID of the deleted dataset.", + "operationId": "Api_data::data_delete", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "ID of the deleted dataset", + "content": { + "application/json": { + "schema": { + "properties": { + "data_delete": { + "$ref": "#/components/schemas/inline_response_200_data_delete" + } + }, + "type": "object" + }, + "example": { + "data_delete": { + "id": "4328" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned\\n- 350 - Please provide API key. In order to remove your content, please authenticate.\\n- 351 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\\n- 352 - Dataset does not exists. The data ID could not be linked to an existing dataset.\\n- 353 - Dataset is not owned by you. The dataset is owned by another user. Hence you cannot delete it.\\n- 354 - Dataset is in use by other content. Can not be deleted. The data is used in tasks or runs. Delete other content before deleting this dataset.\\n- 355 - Deleting dataset failed. Deleting the dataset failed. Please contact support team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data": { + "post": { + "tags": [ + "data" + ], + "summary": "Upload dataset", + "description": "Uploads a dataset. Upon success, it returns the data id.", + "operationId": "Api_data::data_upload", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/data).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "dataset", + "in": "query", + "description": "The actual dataset, being an ARFF file.", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the uploaded dataset", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_data_set": { + "$ref": "#/components/schemas/inline_response_200_1_upload_data_set" + } + }, + "type": "object" + }, + "example": { + "upload_data_set": { + "id": "4328" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n130 - Problem with file uploading. There was a problem with the file upload.\\n131 - Problem validating uploaded description file. The XML description format does not meet the standards.\\n132 - Failed to move the files. Internal server error, please contact API administrators.\\n133 - Failed to make checksum of datafile. Internal server error, please contact API administrators.\\n134 - Failed to insert record in database. Internal server error, please contact API administrators.\\n135 - Please provide description xml.\\n136 - File failed format verification. The uploaded file is not valid according to the selected file format. Please check the file format specification and try again.\\n137 - Please provide API key. In order to share content, please log in or provide your API key.\\n138 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\\n139 - Combination name / version already exists. Leave version out for auto increment\\n140 - Both dataset file and dataset url provided. The system is confused since both a dataset file (post) and a dataset url (xml) are provided. Please remove one.\\n141 - Neither dataset file or dataset url are provided. Please provide either a dataset file as POST variable, or a dataset url in the description XML.\\n142 - Error in processing arff file. Can be a syntax error, or the specified target feature does not exists. For now, we only check on arff files. If a dataset is claimed to be in such a format, and it can not be parsed, this error is returned.\\n143 - Suggested target feature not legal. It is possible to suggest a default target feature (for predictive tasks). However, it should be provided in the data.\\n144 - Unable to update dataset. The dataset with id could not be found in the database. If you upload a new dataset, unset the id.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/status/update/": { + "post": { + "tags": [ + "data" + ], + "summary": "Change the status of a dataset", + "description": "Change the status of a dataset, either 'active' or 'deactivated'", + "operationId": "Api_data::status_update", + "parameters": [ + { + "name": "data_id", + "in": "query", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "status", + "in": "query", + "description": "The status on which to filter the results, either 'active' or 'deactivated'.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "412": { + "description": "Precondition failed. An error code and message are returned.\\n691 - Illegal status\\n692 - Dataset does not exists\\n693 - Dataset is not owned by you\\n694 - Illegal status transition\\n695 - Status update failed\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/features/{id}": { + "get": { + "tags": [ + "data" + ], + "summary": "Get data features", + "description": "Returns the features of a dataset.", + "operationId": "Api_data::data_features", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "All the features of the dataset", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataFeatures" + }, + "example": { + "data_features": { + "feature": [ + { + "index": "0", + "name": "sepallength", + "data_type": "numeric", + "is_target": "false", + "is_ignore": "false", + "is_row_identifier": "false" + }, + { + "index": "1", + "name": "sepalwidth", + "data_type": "numeric", + "is_target": "false", + "is_ignore": "false", + "is_row_identifier": "false" + }, + { + "index": "2", + "name": "petallength", + "data_type": "numeric", + "is_target": "false", + "is_ignore": "false", + "is_row_identifier": "false" + }, + { + "index": "3", + "name": "petalwidth", + "data_type": "numeric", + "is_target": "false", + "is_ignore": "false", + "is_row_identifier": "false" + }, + { + "index": "4", + "name": "class", + "data_type": "nominal", + "is_target": "true", + "is_ignore": "false", + "is_row_identifier": "false" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n270 - Please provide dataset ID.\\n271 - Unknown dataset. Data set with the given data ID was not found (or is not shared with you).\\n272 - No features found. The dataset did not contain any features, or we could not extract them.\\n273 - Dataset not processed yet. The dataset was not processed yet, features are not yet available. Please wait for a few minutes.\\n274 - Dataset processed with error. The feature extractor has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, please contact the API admins.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/features": { + "post": { + "tags": [ + "data" + ], + "summary": "Upload dataset feature description", + "description": "Uploads dataset feature description. Upon success, it returns the data id.", + "operationId": "Api_data::data_features_upload", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.features) and an [XML example](https://www.openml.org/api/v1/xml_example/data.features).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "412": { + "description": "Precondition failed. An error code and message are returned.\\n431 - Dataset already processed\\n432 - Please provide description xml\\n433 - Problem validating uploaded description file\\n434 - Could not find dataset\\n436 - Something wrong with XML, check data id and evaluation engine id\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/qualities/list": { + "get": { + "tags": [ + "data" + ], + "summary": "List all data qualities", + "description": "Returns a list of all data qualities in the system.", + "operationId": "Api_data::data_qualities_list", + "responses": { + "200": { + "description": "A list of data qualities", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataQualityList" + }, + "example": { + "data_qualities_list": { + "quality": [ + "NumberOfClasses", + "NumberOfFeatures", + "NumberOfInstances", + "NumberOfInstancesWithMissingValues", + "NumberOfMissingValues", + "NumberOfNumericFeatures", + "NumberOfSymbolicFeatures" + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned\\n370 - No data qualities available. There are no data qualities in the system.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/qualities/{id}": { + "get": { + "tags": [ + "data" + ], + "summary": "Get data qualities", + "description": "Returns the qualities of a dataset.", + "operationId": "Api_data::data_qualities", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the dataset.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "All the qualities of the dataset", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataQualities" + }, + "example": { + "data_qualities": { + "quality": [ + { + "name": "ClassCount", + "value": "3.0" + }, + { + "name": "ClassEntropy", + "value": "1.584962500721156" + }, + { + "name": "NumberOfClasses", + "value": "3" + }, + { + "name": "NumberOfFeatures", + "value": "5" + }, + { + "name": "NumberOfInstances", + "value": "150" + }, + { + "name": "NumberOfInstancesWithMissingValues", + "value": "0" + }, + { + "name": "NumberOfMissingValues", + "value": "0" + }, + { + "name": "NumberOfNumericFeatures", + "value": "4" + }, + { + "name": "NumberOfSymbolicFeatures", + "value": "0" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n360 - Please provide data set ID\\n361 - Unknown dataset. The data set with the given ID was not found in the database, or is not shared with you.\\n362 - No qualities found. The registered dataset did not contain any calculated qualities.\\n363 - Dataset not processed yet. The dataset was not processed yet, no qualities are available. Please wait for a few minutes.\\n364 - Dataset processed with error. The quality calculator has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, contact the support team.\\n365 - Interval start or end illegal. There was a problem with the interval\\nstart or end.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/qualities": { + "post": { + "tags": [ + "data" + ], + "summary": "Upload dataset qualities", + "description": "Uploads dataset qualities. Upon success, it returns the data id.", + "operationId": "Api_data::data_qualities_upload", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.qualities) and an [XML example](https://www.openml.org/api/v1/xml_example/data.qualities).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "412": { + "description": "Precondition failed. An error code and message are returned.\\n381 - Something wrong with XML, please check did and evaluation_engine_id\\n382 - Please provide description xml\\n383 - Problem validating uploaded description file\\n384 - Dataset not processed yet\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/unprocessed/{data_engine_id}/{order}": { + "get": { + "tags": [ + "data" + ], + "summary": "Get a list of unprocessed datasets", + "description": "This call is for people running their own dataset processing engines. It returns the details of datasets that are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", + "operationId": "Api_data::data_unprocessed", + "parameters": [ + { + "name": "data_engine_id", + "in": "path", + "description": "The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "order", + "in": "path", + "description": "When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of unprocessed datasets", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataUnprocessed" + }, + "example": { + "data_unprocessed": { + "run": [ + { + "did": "1", + "status": "deactivated", + "version": "2", + "name": "anneal", + "format": "ARFF" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n681 - No unprocessed datasets.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/data/qualities/unprocessed/{data_engine_id}/{order}": { + "post": { + "tags": [ + "data" + ], + "summary": "Get a list of datasets with unprocessed qualities", + "description": "This call is for people running their own dataset processing engines. It returns the details of datasets for which certain qualities are not yet processed by the given processing engine. It doesn't process the datasets, it just returns the dataset info.", + "operationId": "Api_data::dataqualities_unprocessed", + "parameters": [ + { + "name": "data_engine_id", + "in": "path", + "description": "The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "order", + "in": "path", + "description": "When there are multiple datasets still to process, this defines which ones to return. Options are 'normal' - the oldest datasets, or 'random'.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "API key to authenticate the user", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "qualities", + "in": "query", + "description": "Comma-separated list of (at least two) quality names, e.g. 'NumberOfInstances,NumberOfFeatures'.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of unprocessed datasets", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataUnprocessed" + }, + "example": { + "data_unprocessed": { + "run": [ + { + "did": "1", + "status": "deactivated", + "version": "2", + "name": "anneal", + "format": "ARFF" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n686 - Please specify the features the evaluation engine wants to calculate (at least 2).\\n687 - No unprocessed datasets according to the given set of meta-features.\\n688 - Illegal qualities.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/estimationprocedure/list": { + "get": { + "tags": [ + "estimationprocedure" + ], + "summary": "List all estimation procedures", + "description": "Returns an array with all model performance estimation procedures in the system.", + "operationId": "Api_estimationprocedure::estimationprocedure_list", + "responses": { + "200": { + "description": "A list of estimation procedures", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EstimationProcedureList" + }, + "example": { + "estimationprocedures": { + "estimationprocedure": [ + { + "id": "1", + "ttid": "1", + "name": "10-fold Crossvalidation", + "type": "crossvalidation", + "repeats": "1", + "folds": "10", + "stratified_sampling": "true" + }, + { + "id": "2", + "ttid": "1", + "name": "5 times 2-fold Crossvalidation", + "type": "crossvalidation", + "repeats": "5", + "folds": "2", + "stratified_sampling": "true" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n500 - No model performance estimation procedures available.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/evaluation/request/{evaluation_engine_id}/{order}": { + "get": { + "tags": [ + "evaluation" + ], + "summary": "Get an unevaluated run", + "description": "This call is for people running their own evaluation engines. It returns the details of a run that is not yet evaluated by the given evaluation engine. It doesn't evaluate the run, it just returns the run info.", + "operationId": "Api_evaluation::evaluation_request", + "parameters": [ + { + "name": "evaluation_engine_id", + "in": "path", + "description": "The ID of the evaluation engine. You get this ID when you register a new evaluation engine with OpenML. The ID of the main evaluation engine is 1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "order", + "in": "path", + "description": "When there are multiple runs still to evaluate, this defines which one to return. Options are 'normal' - the oldest run, 'reverse' - the newest run, or 'random' - a random run.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of evaluations descriptions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluationRequest" + }, + "example": { + "evaluation_request": { + "run": [ + { + "setup_id": "68799271", + "upload_time": "2018-04-03 21:05:38", + "uploader": "1935", + "task_id": "3021", + "run_id": "8943712" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n100 - Function not valid.\\n545 - No unevaluated runs according to the criteria.\\n546 - Illegal filter.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/evaluation/list/{filters}": { + "get": { + "tags": [ + "evaluation" + ], + "summary": "List and filter evaluations", + "description": "List evaluations, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/evaluation/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, uploaders or runs.", + "operationId": "Api_evaluation::evaluation_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /function/{name} - name of the evaluation measure, e.g. area_under_auc or predictive_accuracy. See the OpenML website for the complete list of measures.\r\n /tag/{tag} - returns only evaluations of runs tagged with the given tag.\r\n /run/{ids} - return only evaluations for specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3''\r\n /task/{ids} - return only evaluations for specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3''\r\n /flow/{ids} - return only evaluations for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3''\r\n /setup/{ids} - return only evaluations for specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3''\r\n /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n /per_fold/{true,false} - whether or not to return crossvalidation scores per fold. Defaults to 'false'. Setting it to 'true' leads to large numbers of results, use only for very specific sets of runs.\r\n /sort_order/{asc,desc} - sorts the results by the evaluation value, according to the selected evaluation measure (function)\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of evaluations descriptions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluationList" + }, + "example": { + "evaluations": { + "evaluation": [ + { + "function": "area_under_roc_curve", + "upload_time": "2014-04-06 23:30:40", + "task_id": "68", + "run_id": "1", + "array_data": "[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]", + "value": "0.839359", + "uploader": "1", + "flow_id": "61" + }, + { + "function": "f_measure", + "upload_time": "2014-04-06 23:30:40", + "task_id": "68", + "run_id": "1", + "array_data": "[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]", + "value": "0.600026", + "uploader": "1", + "flow_id": "61" + }, + { + "function": "predictive_accuracy", + "upload_time": "2014-04-06 23:30:40", + "task_id": "68", + "run_id": "1", + "array_data": [], + "value": "0.614634", + "uploader": "1", + "flow_id": "61" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n540 - Please provide at least task, flow or setup, uploader or run, to\\nfilter results, or limit the number of responses.\\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\\n542 - There where no results. Check whether there are runs under the given constraint.\\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\\n544 - Illegal filter specified.\\n545 - Offset specified without limit.\\n546 - Requested result limit too high.\\n547 - Per fold can only be set to value 'true' or 'false'.\\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/evaluationmeasure/list": { + "get": { + "tags": [ + "evaluationmeasure" + ], + "summary": "List all evaluation measures", + "description": "Returns an array with all model evaluation measures in the system.", + "operationId": "Api_evaluationmeasure::evaluationmeasure_list", + "responses": { + "200": { + "description": "A list of evaluation measures", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvaluationMeasureList" + }, + "example": { + "evaluation_measures": { + "measures": { + "measure": [ + "area_under_roc_curve", + "average_cost", + "binominal_test", + "build_cpu_time" + ] + } + } + } + } + } + } + } + } + }, + "/flow/list/{filters}": { + "get": { + "tags": [ + "flow" + ], + "summary": "List and filter flows", + "description": "List flows, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all flows that match the constraints.", + "operationId": "Api_flow::flow_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified.\r\n /tag/{tag} - returns only tasks tagged with the given tag.\r\n /uploader/{id} - return only evaluations uploaded by a specific user, specified by user ID.\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of flows", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FlowList" + }, + "example": { + "flows": { + "flow": [ + { + "id": "65", + "full_name": "weka.RandomForest(1)", + "name": "weka.RandomForest", + "version": "1", + "external_version": "Weka_3.7.10_9186", + "uploader": "1" + }, + { + "id": "66", + "full_name": "weka.IBk(1)", + "name": "weka.IBk", + "version": "1", + "external_version": "Weka_3.7.10_8034", + "uploader": "1" + }, + { + "id": "67", + "full_name": "weka.BayesNet_K2(1)", + "name": "weka.BayesNet_K2", + "version": "1", + "external_version": "Weka_3.7.10_8034", + "uploader": "1" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n500 - No results. There where no matches for the given constraints.\\n501 - Illegal filter specified.\\n502 - Filter values/ranges not properly specified.\\n503 - Can not specify an offset without a limit.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/flow/tag": { + "post": { + "tags": [ + "flow" + ], + "summary": "Tag a flow", + "description": "Tags a flow.", + "operationId": "Api_flow::flow_tag", + "parameters": [ + { + "name": "flow_id", + "in": "query", + "description": "Id of the flow.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the tagged flow", + "content": { + "application/json": { + "schema": { + "properties": { + "flow_tag": { + "$ref": "#/components/schemas/inline_response_200_12_flow_tag" + } + }, + "type": "object" + }, + "example": { + "flow_tag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\\n474 - Internal error tagging the entity. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/flow/untag": { + "post": { + "tags": [ + "flow" + ], + "summary": "Untag a flow", + "description": "Untags a flow.", + "operationId": "Api_flow::flow_untag", + "parameters": [ + { + "name": "flow_id", + "in": "query", + "description": "Id of the flow.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the untagged flow", + "content": { + "application/json": { + "schema": { + "properties": { + "flow_untag": { + "$ref": "#/components/schemas/inline_response_200_13_flow_untag" + } + }, + "type": "object" + }, + "example": { + "flow_untag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\\nby another user. Hence you cannot delete it.\\n479 - Internal error removing the tag. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/flow/exists/{name}/{version}": { + "get": { + "tags": [ + "flow" + ], + "summary": "Check whether flow exists", + "description": "Checks whether a flow with the given name and (external) version exists.", + "operationId": "Api_flow::flow_exists", + "parameters": [ + { + "name": "name", + "in": "path", + "description": "The name of the flow.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "version", + "in": "path", + "description": "The external version of the flow", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of flows", + "content": { + "application/json": { + "schema": { + "properties": { + "flow_exists": { + "$ref": "#/components/schemas/inline_response_200_10_flow_exists" + } + }, + "type": "object" + }, + "example": { + "flow_exists": { + "exists": "true", + "id": "65" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n330 - Mandatory fields not present. Please provide name and external_version.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/flow/{id}": { + "get": { + "tags": [ + "flow" + ], + "summary": "Get flow description", + "description": "Returns information about a flow. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + "operationId": "Api_flow::flow", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the flow.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A flow description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Flow" + }, + "example": { + "flow": { + "id": "100", + "uploader": "1", + "name": "weka.J48", + "version": "2", + "external_version": "Weka_3.7.5_9117", + "description": "...", + "upload_date": "2014-04-23 18:00:36", + "language": "Java", + "dependencies": "Weka_3.7.5", + "parameter": [ + { + "name": "A", + "data_type": "flag", + "default_value": [], + "description": "Laplace smoothing..." + }, + { + "name": "C", + "data_type": "option", + "default_value": "0.25", + "description": "Set confidence threshold..." + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n180 - Please provide flow id.\\n181 - Unknown flow. The flow with the given ID was not found in the database.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "tags": [ + "flow" + ], + "summary": "Delete a flow", + "description": "Deletes a flow. Upon success, it returns the ID of the deleted flow.", + "operationId": "Api_flow::flow_delete", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the flow.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "api_key", + "in": "query", + "description": "API key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "ID of the deleted flow", + "content": { + "application/json": { + "schema": { + "properties": { + "flow_delete": { + "$ref": "#/components/schemas/inline_response_200_8_flow_delete" + } + }, + "type": "object" + }, + "example": { + "flow_delete": { + "id": "4328" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n320 - Please provide API key. In order to remove your content, please authenticate.\\n321 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\\n322 - Flow does not exists. The flow ID could not be linked to an existing flow.\\n323 - Flow is not owned by you. The flow is owned by another user. Hence you cannot delete it.\\n324 - Flow is in use by other content. Can not be deleted. The flow is used in runs, evaluations or as a component of another flow. Delete other content before deleting this flow.\\n325 - Deleting flow failed. Deleting the flow failed. Please contact\\nsupport team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/flow": { + "post": { + "tags": [ + "flow" + ], + "summary": "Upload a flow", + "description": "Uploads a flow. Upon success, it returns the flow id.", + "operationId": "Api_flow::flow_upload", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "flow", + "in": "query", + "description": "The actual flow, being a source (or binary) file.", + "required": false, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the uploaded flow", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_flow": { + "$ref": "#/components/schemas/inline_response_200_9_upload_flow" + } + }, + "type": "object" + }, + "example": { + "upload_flow": { + "id": "2520" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n160 - Error in file uploading. There was a problem with the file upload.\\n161 - Please provide description xml.\\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\\n164 - Flow already stored in database. Please change name or version number\\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\\n166 - Failed to add flow to database. Internal server error, please contact API administrators\\n167 - Illegal files uploaded. An non required file was uploaded.\\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\\n171 - Flow already exists. This flow is already in the database\\n172 - XSD not found. Please contact API support team\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/tag": { + "post": { + "tags": [ + "run" + ], + "summary": "Tag a run", + "description": "Tags a run.", + "operationId": "Api_run::run_tag", + "parameters": [ + { + "name": "run_id", + "in": "query", + "description": "Id of the run.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the tagged run", + "content": { + "application/json": { + "schema": { + "properties": { + "run_tag": { + "$ref": "#/components/schemas/inline_response_200_19_run_tag" + } + }, + "type": "object" + }, + "example": { + "run_tag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\\n474 - Internal error tagging the entity. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/untag": { + "post": { + "tags": [ + "run" + ], + "summary": "Untag a run", + "description": "Untags a run.", + "operationId": "Api_run::run_untag", + "parameters": [ + { + "name": "run_id", + "in": "query", + "description": "Id of the run.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the untagged run", + "content": { + "application/json": { + "schema": { + "properties": { + "run_untag": { + "$ref": "#/components/schemas/inline_response_200_20_run_untag" + } + }, + "type": "object" + }, + "example": { + "run_untag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\\n479 - Internal error removing the tag. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/list/{filters}": { + "get": { + "tags": [ + "run" + ], + "summary": "List and filter runs", + "description": "List runs, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/run/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all runs that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, or uploaders.", + "operationId": "Api_run::run_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /tag/{tag} - return only runs tagged with the given tag.\r\n /run/{ids} - return only specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3''\r\n /task/{ids} - return only runs on specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3''\r\n /flow/{ids} - return only runs on specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3''\r\n /setup/{ids} - return only runs with specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3''\r\n /uploader/{ids} - return only runs uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of runs descriptions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunList" + }, + "example": { + "runs": { + "run": [ + { + "upload_time": "2014-04-06 23:30:40", + "task_id": "28", + "run_id": "100", + "error_message": [], + "setup_id": "12", + "uploader": "1", + "flow_id": "67" + }, + { + "upload_time": "2014-04-06 23:30:40", + "task_id": "48", + "run_id": "101", + "error_message": [], + "setup_id": "6", + "uploader": "1", + "flow_id": "61" + }, + { + "upload_time": "2014-04-06 23:30:40", + "task_id": "41", + "run_id": "102", + "error_message": [], + "setup_id": "3", + "uploader": "1", + "flow_id": "58" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n510 - Please provide at least task, flow or setup, uploader or run, to filter results, or limit the number of responses. The number of runs is huge. Please limit the result space.\\n511 - Input not safe. The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\\n512 - There where no results. Check whether there are runs under the given constraint.\\n513 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\\n514 - Illegal filter specified.\\n515 - Offset specified without limit.\\n516 - Requested result limit too high.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/{id}": { + "get": { + "tags": [ + "run" + ], + "summary": "Get run description", + "description": "Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.", + "operationId": "Api_run::run", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the run.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A run description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Run" + }, + "example": { + "run": { + "run_id": "100", + "uploader": "1", + "uploader_name": "Jan van Rijn", + "task_id": "28", + "task_type": "Supervised Classification", + "task_evaluation_measure": "predictive_accuracy", + "flow_id": "67", + "flow_name": "weka.BayesNet_K2(1)", + "setup_string": "weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5", + "parameter_setting": [ + { + "name": "D", + "value": "true" + }, + { + "name": "Q", + "value": "weka.classifiers.bayes.net.search.local.K2" + }, + { + "name": "P", + "value": "1" + }, + { + "name": "S", + "value": "BAYES" + } + ], + "input_data": { + "dataset": { + "did": "28", + "name": "optdigits", + "url": "https:\\\\/\\\\/www.openml.org\\\\/data\\\\/download\\\\/28\\\\/dataset_28_optdigits.arff" + } + }, + "output_data": { + "file": [ + { + "did": "48838", + "file_id": "261", + "name": "description", + "url": "https:\\\\/\\\\/www.openml.org\\\\/data\\\\/download\\\\/261\\\\/weka_generated_run935374685998857626.xml" + }, + { + "did": "48839", + "file_id": "262", + "name": "predictions", + "url": "https:\\\\/\\\\/www.openml.org\\\\/data\\\\/download\\\\/262\\\\/weka_generated_predictions576954524972002741.arff" + } + ], + "evaluation": [ + { + "name": "area_under_roc_curve", + "flow_id": "4", + "value": "0.990288", + "array_data": "[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]" + }, + { + "name": "confusion_matrix", + "flow_id": "10", + "array_data": "[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]" + }, + { + "name": "f_measure", + "flow_id": "12", + "value": "0.922723", + "array_data": "[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]" + }, + { + "name": "kappa", + "flow_id": "13", + "value": "0.913601" + } + ] + } + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n220 - Please provide run ID. In order to view run details, please provide the run ID.\\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "tags": [ + "run" + ], + "summary": "Delete run", + "description": "Deletes a run. Upon success, it returns the ID of the deleted run.", + "operationId": "Api_run::run_delete", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the run.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "api_key", + "in": "query", + "description": "API key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "ID of the deleted run", + "content": { + "application/json": { + "schema": { + "properties": { + "data_delete": { + "$ref": "#/components/schemas/inline_response_200_17_data_delete" + } + }, + "type": "object" + }, + "example": { + "run_delete": { + "id": "2520" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n390 - Please provide API key. In order to remove your content, please authenticate.\\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\\n392 - Run does not exists. The run ID could not be linked to an existing run.\\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/reset/{id}": { + "get": { + "tags": [ + "run" + ], + "summary": "Resets a run.", + "description": "Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.", + "operationId": "Api_run::run_reset", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Run ID.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the evaluated run", + "content": { + "application/json": { + "schema": { + "properties": { + "run_reset": { + "$ref": "#/components/schemas/inline_response_200_21_upload_flow" + } + }, + "type": "object" + }, + "example": { + "run_reset": { + "id": "2520" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n412 - Run does not exist\\n413 - Run is not owned by you\\n394 - Resetting run failed\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run": { + "post": { + "tags": [ + "run" + ], + "summary": "Upload run", + "description": "Uploads a run. Upon success, it returns the run id.", + "operationId": "Api_run::run_upload", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "predictions", + "in": "query", + "description": "The predictions generated by the run", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "model_readable", + "in": "query", + "description": "The human-readable model generated by the run", + "required": false, + "schema": { + "type": "file" + } + }, + { + "name": "model_serialized", + "in": "query", + "description": "The serialized model generated by the run", + "required": false, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the uploaded run", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_flow": { + "$ref": "#/components/schemas/inline_response_200_18_upload_flow" + } + }, + "type": "object" + }, + "example": { + "upload_run": { + "id": "2520" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\\n202 - Please provide run XML.\\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\\n204 - Unknown task. The task with the given ID was not found in the database.\\n205 - Unknown flow. The flow with the given ID was not found in the database.\\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\\n207 - File upload failed. One of the files uploaded has a problem.\\n208 - Error inserting setup record. Please contact api administrators\\n210 - Unable to store run. Please contact api administrators.\\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\\n212 - Unable to store file. Please contact api administrators.\\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\\n214 - Unable to store input setting. Please contact API support team.\\n215 - Unable to evaluate predictions. Please contact API support team.\\n216 - Error thrown by Java Application. Additional information field is provided.\\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\\n219 - Error reading the XML document. The XML description file could not be verified.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/trace/{id}": { + "get": { + "tags": [ + "run" + ], + "summary": "Get run trace", + "description": "Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.", + "operationId": "Api_run::run_trace", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the run.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A run trace description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunTrace" + }, + "example": { + "trace": { + "run_id": "573055", + "trace_iteration": { + "repeat": "0", + "fold": "0", + "iteration": "1", + "setup_string": { + "parameter_minNumObj": "1", + "parameter_confidenceFactor": "0.25" + }, + "evaluation": "94.074074", + "selected": "true" + } + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n570 - No successful trace associated with this run\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "tags": [ + "run" + ], + "summary": "Upload run trace", + "description": "Uploads a run trace. Upon success, it returns the run id.", + "operationId": "Api_run::run_trace_upload", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the run.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "description", + "in": "query", + "description": "An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the run with the trace", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_flow": { + "$ref": "#/components/schemas/inline_response_200_23_upload_flow" + } + }, + "type": "object" + }, + "example": { + "run_trace": { + "id": "2520" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n561 - Problem with uploaded trace file.\\n562 - Problem validating xml trace file.\\n563 - Problem loading xml trace file.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/run/evaluate": { + "post": { + "tags": [ + "run" + ], + "summary": "Uploads a run evaluation", + "description": "Uploads a run evaluation. When successful, it returns the run id.", + "operationId": "Api_run::run_evaluate", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the run evaluation.Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.evaluate) and an [XML example](https://www.openml.org/api/v1/xml_example/run.evaluate).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the evaluated run", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_flow": { + "$ref": "#/components/schemas/inline_response_200_21_upload_flow" + } + }, + "type": "object" + }, + "example": { + "run_evaluate": { + "id": "2520" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n422 - Upload problem description XML\\n423 - Problem validating uploaded description file\\n424 - Problem opening description xml\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/setup/tag": { + "post": { + "tags": [ + "setup" + ], + "summary": "Tag a setup", + "description": "Tags a setup.", + "operationId": "Api_setup::setup_tag", + "parameters": [ + { + "name": "setup_id", + "in": "query", + "description": "Id of the setup.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the tagged setup", + "content": { + "application/json": { + "schema": { + "properties": { + "flow_tag": { + "$ref": "#/components/schemas/inline_response_200_15_flow_tag" + } + }, + "type": "object" + }, + "example": { + "setup_tag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\\n474 - Internal error tagging the entity. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/setup/untag": { + "post": { + "tags": [ + "setup" + ], + "summary": "Untag a setup", + "description": "Untags a setup.", + "operationId": "Api_setup::setup_untag", + "parameters": [ + { + "name": "setup_id", + "in": "query", + "description": "Id of the setup.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the untagged setup", + "content": { + "application/json": { + "schema": { + "properties": { + "flow_untag": { + "$ref": "#/components/schemas/inline_response_200_16_flow_untag" + } + }, + "type": "object" + }, + "example": { + "setup_untag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\\nby another user. Hence you cannot delete it.\\n479 - Internal error removing the tag. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/setup/{id}": { + "get": { + "tags": [ + "setup" + ], + "summary": "Get a hyperparameter setup", + "description": "Returns information about a setup. The information includes the list of hyperparameters, with name, value, and default value.", + "operationId": "Api_setup::setup", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A setup description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Setup" + }, + "example": { + "setup_parameters": { + "flow_id": "59", + "parameter": [ + { + "full_name": "weka.JRip(1)_F", + "parameter_name": "F", + "data_type": "option", + "default_value": "3", + "value": "3" + }, + { + "full_name": "weka.JRip(1)_N", + "parameter_name": "N", + "data_type": "option", + "default_value": "2.0", + "value": "2.0" + }, + { + "full_name": "weka.JRip(1)_O", + "parameter_name": "O", + "data_type": "option", + "default_value": "2", + "value": "2" + }, + { + "full_name": "weka.JRip(1)_S", + "parameter_name": "S", + "data_type": "option", + "default_value": "1", + "value": "1" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n280 - Please provide setup ID. In order to view setup details, please provide the run ID\\n281 - Setup not found. The setup ID was invalid, or setup does not exist (anymore).\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "tags": [ + "setup" + ], + "summary": "Delete setup", + "description": "Deletes a setup. Upon success, it returns the ID of the deleted setup.", + "operationId": "Api_setup::setup_delete", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the setup.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "ID of the deleted setup", + "content": { + "application/json": { + "schema": { + "properties": { + "study_delete": { + "$ref": "#/components/schemas/inline_response_200_14_study_delete" + } + }, + "type": "object" + }, + "example": { + "setup_delete": { + "id": "1" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n401 - Authentication failed. Please provide API key. In order to remove your content, please authenticate.\\n402 - Setup does not exists. The setup ID could not be linked to an existing setup.\\n404 - Setup deletion failed. Setup is in use by other content (runs, schedules, etc). Can not be deleted.\\n405 - Setup deletion failed. Please try again later.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/setup/list/{filters}": { + "get": { + "tags": [ + "setup" + ], + "summary": "List and filter setups", + "description": "List setups, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/setup/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all evaluations that match the constraints. A maximum of 1,000 results are returned at a time, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain flows, setups, or tags.", + "operationId": "Api_setup::setup_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /tag/{tag} - returns only setups tagged with the given tag.\r\n /flow/{ids} - return only setups for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3''\r\n /setup/{ids} - return only specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of setup descriptions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetupList" + }, + "example": { + "setups": { + "setup": [ + { + "setup_id": "10", + "flow_id": "65", + "parameter": [ + { + "id": "4144", + "flow_id": "65", + "flow_name": "weka.RandomForest", + "full_name": "weka.RandomForest(1)_I", + "parameter_name": "I", + "data_type": "option", + "default_value": "10", + "value": "10" + }, + { + "id": "4145", + "flow_id": "65", + "flow_name": "weka.RandomForest", + "full_name": "weka.RandomForest(1)_K", + "parameter_name": "K", + "data_type": "option", + "default_value": "0", + "value": "0" + } + ] + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n670 - Please specify at least one filter.\\n671 - Illegal filter.\\n672 - Illegal filter input.\\n673 - Result set too big. Please use one of the filters or the limit option.\\n674 - No results, please check the filter.\\n675 - Cannot specify offset without limit.\\n676 - Requested result limit too high.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/study": { + "post": { + "tags": [ + "study" + ], + "summary": "Create new study", + "description": "Creates a new study. Upon success, it returns the study id.", + "operationId": "Api_study::study_create", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the study. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.study.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/study).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the uploaded study", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_study": { + "$ref": "#/components/schemas/inline_response_200_25_upload_study" + } + }, + "type": "object" + }, + "example": { + "upload_study": { + "id": "4328" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n1031 - Description file not present. Please upload the study description.\\n1032 - Problem validating uploaded description file. The XML description format does not meet the standards. See the XSD schema.\\n1033 - Illegal main entity type. Currently only collections of tasks and can be created.\\n1034 - Linked entities are not of the correct type fot this study.\\n1035 - Benchmark suites can only be linked to run studies.\\n1036 - Referred benchmark suite cannot be found.\\n1037 - Referred benchmark suite should be a task collection.\\n1038 - Study alias is not unique.\\n1039 - Dataset insertion problem. Please contact the administrators.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/study/{id}/attach": { + "post": { + "tags": [ + "study" + ], + "summary": "Attach a new entity to a study", + "description": "Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.", + "operationId": "Api_study::study_attach", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the study. Supplied in the URL path.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "ids", + "in": "query", + "description": "Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Properties of the updated study", + "content": { + "application/json": { + "schema": { + "properties": { + "study_attach": { + "$ref": "#/components/schemas/inline_response_200_26_study_attach" + } + }, + "type": "object" + }, + "example": { + "study_attach": { + "id": "1", + "main_entity_type": "task", + "linked_entities": "5" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n1041 - Could not find study. Check the study ID in your request.\\n1042 - Cannnot attach entities to legacy studies.\\n1043 - Please provide POST field 'ids'.\\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/study/{id}/detach": { + "post": { + "tags": [ + "study" + ], + "summary": "Detach an entity from a study", + "description": "Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.", + "operationId": "Api_study::study_detach", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the study.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "ids", + "in": "query", + "description": "Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Properties of the updated study", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_study": { + "$ref": "#/components/schemas/inline_response_200_26_study_attach" + } + }, + "type": "object" + }, + "example": { + "study_detach": { + "id": "1", + "main_entity_type": "task", + "linked_entities": "5" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n1041 - Could not find study. Check the study ID in your request.\\n1042 - Cannot attach entities to legacy studies.\\n1043 - Please provide POST field 'ids'.\\n1044 - Please ensure that the 'ids' in the POST field is a list of natural numbers.\\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/study/{id}": { + "get": { + "tags": [ + "study" + ], + "summary": "Get study description by study id or alias", + "description": "Returns information about the study with the given id or alias.", + "operationId": "Api_study::study_get", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID or alias of the study.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A study description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Study" + }, + "example": { + "study": { + "id": "99", + "main_entity_type": "task", + "name": "CC18 benchmark suite", + "description": "CC18 benchmark suite", + "creation_date": "2019-02-16T17:35:58", + "creator": "1159", + "data": { + "data_id": [ + "1", + "2", + "3" + ] + }, + "tasks": { + "task_id": [ + "1", + "2", + "3" + ] + } + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n601 - Unknown study. The study with the given id or alias was not found in the database\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "tags": [ + "study" + ], + "summary": "Delete study", + "description": "Deletes a study. Upon success, it returns the ID of the deleted study.", + "operationId": "Api_study::study_delete", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the study.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "ID of the deleted study", + "content": { + "application/json": { + "schema": { + "properties": { + "study_delete": { + "$ref": "#/components/schemas/inline_response_200_24_study_delete" + } + }, + "type": "object" + }, + "example": { + "study_delete": { + "id": "1" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n591 - Please provide API key. In order to remove your content, please authenticate.\\n592 - Study does not exists. The study ID could not be linked to an existing study.\\n593 - Study deletion failed. Please try again later.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/study/list/{filters}": { + "get": { + "tags": [ + "study" + ], + "summary": "List all studies (collections of items)", + "description": "List studies, optionally filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form '/study/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all studies that match the constraints.", + "operationId": "Api_study::study_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /main_entity_type/{type} - only return studies collecting entities of a given type (e.g. 'task' or 'run').\r\n /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of studies", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StudyList" + }, + "example": { + "study_list": { + "study": [ + { + "id": "1", + "alias": "Study_1", + "name": "A large-scale comparison of classification algorithms", + "creation_date": "2017-07-20 15:51:20", + "creator": "2" + }, + { + "id": "2", + "alias": "Study_2", + "name": "Fast Algorithm Selection using Learning Curves", + "creation_date": "2017-07-20 15:51:20", + "creator": "2" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/task/tag": { + "post": { + "tags": [ + "task" + ], + "summary": "Tag a task", + "description": "Tags a task.", + "operationId": "Api_task::task_tag", + "parameters": [ + { + "name": "task_id", + "in": "query", + "description": "Id of the task.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The id of the tagged task", + "content": { + "application/json": { + "schema": { + "properties": { + "task_tag": { + "$ref": "#/components/schemas/inline_response_200_6_task_tag" + } + }, + "type": "object" + }, + "example": { + "task_tag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n470 - In order to add a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\\n471 - Entity not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\\n472 - Entity already tagged by this tag. The entity {dataset, task, flow, run} already had this tag.\\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\\n474 - Internal error tagging the entity. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/task/untag": { + "post": { + "tags": [ + "task" + ], + "summary": "Untag a task", + "description": "Untags a task.", + "operationId": "Api_task::task_untag", + "parameters": [ + { + "name": "task_id", + "in": "query", + "description": "Id of the task.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "tag", + "in": "query", + "description": "Tag name", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A the features of the task", + "content": { + "application/json": { + "schema": { + "properties": { + "task_untag": { + "$ref": "#/components/schemas/inline_response_200_7_task_untag" + } + }, + "type": "object" + }, + "example": { + "task_untag": { + "id": "2" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\\n476 - Entity {dataset, task, flow, run} not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\\n477 - Tag not found. The provided tag is not associated with the entity {dataset, task, flow, run}.\\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\\n479 - Internal error removing the tag. Please contact OpenML Team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/task/list/{filters}": { + "get": { + "tags": [ + "task" + ], + "summary": "List and filter tasks", + "description": "List tasks, possibly filtered by a range of properties from the task itself or from the underlying dataset. Any number of properties can be combined by listing them one after the other in the form '/task/list/{filter}/{value}/{filter}/{value}/...' Returns an array with all tasks that match the constraints.", + "operationId": "Api_task::task_list", + "parameters": [ + { + "name": "filters", + "in": "path", + "description": "Any combination of these filters\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified.\r\n /status/{status} - returns only tasks with a given status, either 'active', 'deactivated', or 'in_preparation'.\r\n /type/{type_id} - returns only tasks with a given task type id. See the list of task types of the ID's (e.g. 1 = Supervised Classification).\r\n /tag/{tag} - returns only tasks tagged with the given tag.\r\n /data_tag/{tag} - returns only tasks for which the underlying dataset is tagged with the given tag.\r\n /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'.\r\n ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of tasks with the given tag", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskList" + }, + "example": { + "task": { + "task": [ + { + "task_id": "1", + "task_type": "Supervised Classification", + "did": "1", + "name": "anneal", + "status": "active", + "format": "ARFF", + "input": [ + { + "name": "estimation_procedure", + "value": "1" + }, + { + "name": "evaluation_measures", + "value": "predictive_accuracy" + }, + { + "name": "source_data", + "value": "1" + }, + { + "name": "target_feature", + "value": "class" + } + ], + "quality": [ + { + "name": "MajorityClassSize", + "value": "684" + }, + { + "name": "MaxNominalAttDistinctValues", + "value": "10.0" + }, + { + "name": "MinorityClassSize", + "value": "0" + }, + { + "name": "NumBinaryAtts", + "value": "14.0" + }, + { + "name": "NumberOfClasses", + "value": "6" + }, + { + "name": "NumberOfFeatures", + "value": "39" + }, + { + "name": "NumberOfInstances", + "value": "898" + }, + { + "name": "NumberOfInstancesWithMissingValues", + "value": "0" + }, + { + "name": "NumberOfMissingValues", + "value": "0" + }, + { + "name": "NumberOfNumericFeatures", + "value": "6" + }, + { + "name": "NumberOfSymbolicFeatures", + "value": "32" + } + ], + "tag": [ + "basic", + "study_1", + "study_7", + "under100k", + "under1m" + ] + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n480 - Illegal filter specified.\\n481 - Filter values/ranges not properly specified.\\n482 - No results. There where no matches for the given constraints.\\n483 - Can not specify an offset without a limit.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/task/{id}": { + "get": { + "tags": [ + "task" + ], + "summary": "Get task description", + "description": "Returns information about a task. The information includes the task type, input data, train/test sets, and more.", + "operationId": "Api_task::task", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the task.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A task description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Task" + }, + "example": { + "task": { + "task_id": "1", + "task_type": "Supervised Classification", + "input": [ + { + "name": "source_data", + "data_set": { + "data_set_id": "1", + "target_feature": "class" + } + }, + { + "name": "estimation_procedure", + "estimation_procedure": { + "type": "crossvalidation", + "data_splits_url": "https://www.openml.org/api_splits/get/1/Task_1_splits.arff", + "parameter": [ + { + "name": "number_repeats", + "value": "1" + }, + { + "name": "number_folds", + "value": "10" + }, + { + "name": "percentage" + }, + { + "name": "stratified_sampling", + "value": "true" + } + ] + } + }, + { + "name": "cost_matrix", + "cost_matrix": [] + }, + { + "name": "evaluation_measures", + "evaluation_measures": { + "evaluation_measure": "predictive_accuracy" + } + } + ], + "output": { + "name": "predictions", + "predictions": { + "format": "ARFF", + "feature": [ + { + "name": "repeat", + "type": "integer" + }, + { + "name": "fold", + "type": "integer" + }, + { + "name": "row_id", + "type": "integer" + }, + { + "name": "confidence.classname", + "type": "numeric" + }, + { + "name": "prediction", + "type": "string" + } + ] + } + }, + "tag": [ + "basic", + "study_1", + "under100k", + "under1m" + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n150 - Please provide task_id.\\n151 - Unknown task. The task with the given id was not found in the database\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "tags": [ + "task" + ], + "summary": "Delete task", + "description": "Deletes a task. Upon success, it returns the ID of the deleted task.", + "operationId": "Api_task::task_delete", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Id of the task.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "ID of the deleted task", + "content": { + "application/json": { + "schema": { + "properties": { + "task_delete": { + "$ref": "#/components/schemas/inline_response_200_4_task_delete" + } + }, + "type": "object" + }, + "example": { + "task_delete": { + "id": "4328" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n450 - Please provide API key. In order to remove your content, please authenticate.\\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\\n452 - Task does not exists. The task ID could not be linked to an existing task.\\n454 - Task is executed in some runs. Delete these first.\\n455 - Deleting the task failed. Please contact support team.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/task": { + "post": { + "tags": [ + "task" + ], + "summary": "Upload task", + "description": "Uploads a task. Upon success, it returns the task id.", + "operationId": "Api_task::task_upload", + "parameters": [ + { + "name": "description", + "in": "query", + "description": "An XML file describing the task. Only name, description, and task format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.task.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/task).", + "required": true, + "schema": { + "type": "file" + } + }, + { + "name": "api_key", + "in": "query", + "description": "Api key to authenticate the user", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Id of the uploaded task", + "content": { + "application/json": { + "schema": { + "properties": { + "upload_task": { + "$ref": "#/components/schemas/inline_response_200_5_upload_task" + } + }, + "type": "object" + }, + "example": { + "upload_task": { + "id": "4328" + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n530 - Description file not present. Please upload the task description.\\n531 - Internal error. Please contact api support team\\n532 - Problem validating uploaded description file. The XML description format does not meet the standards\\n533 - Task already exists.\\n534 - Error creating the task.\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/tasktype/list": { + "get": { + "tags": [ + "tasktype" + ], + "summary": "List all task types", + "description": "Returns an array with all task types in the system.", + "responses": { + "default": { + "description": "Unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "200": { + "description": "A task description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskTypeList" + }, + "example": { + "task_types": { + "task_type": [ + { + "id": "1", + "name": "Supervised Classification", + "description": "In supervised classification, you are given ...", + "creator": "Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl" + }, + { + "id": "2", + "name": "Supervised Regression", + "description": "Given a dataset with a numeric target ...", + "creator": "Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl" + } + ] + } + } + } + } + } + } + } + }, + "/tasktype/{id}": { + "get": { + "tags": [ + "tasktype" + ], + "summary": "Get task type description", + "description": "Returns information about a task type. The information includes a description, the given inputs and the expected outputs.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the task.", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A task type description", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskType" + }, + "example": { + "task_type": { + "id": "1", + "name": "Supervised Classification", + "description": "In supervised classification, you are given an input dataset in which instances are labeled with a certain class. The goal is to build a model that predicts the class for future unlabeled instances. The model is evaluated using a train-test procedure, e.g. cross-validation.

\\\r\n * \\\r\n * To make results by different users comparable, you are given the exact train-test folds to be used, and you need to return at least the predictions generated by your model for each of the test instances. OpenML will use these predictions to calculate a range of evaluation measures on the server.

\\\r\n * \\\r\n * You can also upload your own evaluation measures, provided that the code for doing so is available from the implementation used. For extremely large datasets, it may be infeasible to upload all predictions. In those cases, you need to compute and provide the evaluations yourself.

\\\r\n * \\\r\n * Optionally, you can upload the model trained on all the input data. There is no restriction on the file format, but please use a well-known format or PMML.", + "creator": [ + "Joaquin Vanschoren", + "Jan van Rijn", + "Luis Torgo", + "Bernd Bischl" + ], + "contributor": [ + "Bo Gao", + "Simon Fischer", + "Venkatesh Umaashankar", + "Michael Berthold", + "Bernd Wiswedel", + "Patrick Winter" + ], + "creation_date": "2013-01-24 00:00:00", + "input": [ + { + "name": "source_data", + "requirement": "required", + "data_type": "numeric" + }, + { + "name": "target_feature", + "requirement": "required", + "data_type": "string" + }, + { + "name": "estimation_procedure", + "requirement": "required", + "data_type": "numeric" + }, + { + "name": "cost_matrix", + "data_type": "json" + }, + { + "name": "custom_testset", + "data_type": "json" + }, + { + "name": "evaluation_measures", + "data_type": "string" + } + ] + } + } + } + } + }, + "412": { + "description": "Precondition failed. An error code and message are returned.\\n240 - Please provide task type ID.\\n241 - Unknown task type. The task type with the given id was not found in the database\\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/user/list": { + "get": { + "tags": [ + "user" + ], + "summary": "List all users by user id", + "description": "Returns an array with all user ids and names.", + "operationId": "Api_user::username_list", + "responses": { + "200": { + "description": "A list of users", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserList" + }, + "example": { + "users": { + "user": [ + { + "id": "1", + "username": "janvanrijn@gmail.com" + }, + { + "id": "2", + "username": "joaquin.vanschoren@gmail.com" + } + ] + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "inline_response_200_16_flow_untag": { + "properties": { + "id": { + "description": "ID of the untagged setup", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationMeasureList_evaluation_measures_measures": { + "properties": { + "measure": { + "description": "The evaluation measure names", + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "inline_response_200_10_flow_exists": { + "properties": { + "id": { + "description": "The id of the flow with the given name and (external) version", + "type": "string" + }, + "exists": { + "description": "true or false", + "type": "string" + } + }, + "type": "object" + }, + "Data_data_set_description": { + "properties": { + "default_target_attribute": { + "description": "For tabular data, the name of the column that is typically used as the target attribute for that data set", + "type": "string" + }, + "upload_date": { + "description": "The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss", + "type": "string" + }, + "version_label": { + "description": "The version of the dataset, as defined by the uploader, for reference. Can be any format as long as it is unique.", + "type": "string" + }, + "description": { + "description": "Wiki description of the dataset, in (Git flavoured) markdown format", + "type": "string" + }, + "format": { + "description": "Data format, for instance ARFF", + "type": "string" + }, + "url": { + "description": "The URL where the data can be downloaded", + "type": "string" + }, + "tag": { + "description": "Tags added by OpenML users. Includes study tags in the form `study_1`", + "type": "array", + "items": { + "type": "string" + } + }, + "visibility": { + "description": "Who can see the dataset. For instance `public`.", + "type": "string" + }, + "md5_checksum": { + "description": "Checksum to verify downloads of the dataset", + "type": "string" + }, + "version": { + "description": "The version of the dataset, set by OpenML. A positive integer", + "type": "string" + }, + "status": { + "description": "active, in_preparation, or deactivated", + "type": "string" + }, + "file_id": { + "description": "The ID of the dataset file stored on the OpenML server", + "type": "string" + }, + "licence": { + "description": "The licence granted for using the dataset, for instance Public or CC-BY", + "type": "string" + }, + "original_data_url": { + "description": "The URL where the original data is hosted.", + "type": "string" + }, + "id": { + "description": "ID of the dataset, a positive integer", + "type": "string" + }, + "name": { + "description": "The name of the dataset", + "type": "string" + } + }, + "type": "object" + }, + "DataList_data_dataset": { + "properties": { + "did": { + "description": "The dataset ID", + "type": "string" + }, + "status": { + "description": "The dataset status, either in_preparation, active, or deactivated", + "type": "string" + }, + "quality": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataList_data_quality" + } + }, + "name": { + "description": "The dataset name", + "type": "string" + }, + "format": { + "description": "The data format of the dataset, e.g. ARFF", + "type": "string" + } + }, + "type": "object" + }, + "Task_task_description_estimation_procedure": { + "properties": { + "parameter": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Task_task_description_estimation_procedure_parameter" + } + }, + "type": { + "description": "The type of procedure, e.g. crossvalidation", + "type": "string" + }, + "data_splits_url": { + "description": "The url where the data splits can be downloaded", + "type": "string" + } + }, + "type": "object" + }, + "DataList_data_quality": { + "properties": { + "name": { + "description": "The name of the property", + "type": "string" + }, + "value": { + "description": "The value of the property", + "type": "string" + } + }, + "type": "object" + }, + "FlowList_flows_flow": { + "properties": { + "full_name": { + "description": "The full flow name (name + internal version number)", + "type": "string" + }, + "external_version": { + "description": "The external flow version", + "type": "string" + }, + "version": { + "description": "The internal flow version", + "type": "string" + }, + "uploader": { + "description": "The ID of the person who uploaded the flow", + "type": "string" + }, + "id": { + "description": "The flow ID", + "type": "string" + }, + "name": { + "description": "The flow name", + "type": "string" + } + }, + "type": "object" + }, + "Run_run_description_input_data_dataset": { + "properties": { + "did": { + "description": "The id of the dataset", + "type": "string" + }, + "url": { + "description": "The download url of the dataset", + "type": "string" + }, + "name": { + "description": "The name of the dataset", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationList_evaluations": { + "properties": { + "evaluation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EvaluationList_evaluations_evaluation" + } + } + }, + "type": "object" + }, + "inline_response_200_14_study_delete": { + "properties": { + "id": { + "description": "ID of the deleted setup, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "DataUnprocessed": { + "properties": { + "data_unprocessed": { + "$ref": "#/components/schemas/DataUnprocessed_data_unprocessed" + } + }, + "type": "object" + }, + "DataUnprocessed_data_unprocessed_dataset": { + "properties": { + "did": { + "description": "ID of the dataset a positive integer", + "type": "string" + }, + "status": { + "description": "Status of the dataset", + "type": "string" + }, + "version": { + "description": "Version of the dataset, a positive integer", + "type": "string" + }, + "name": { + "description": "The name of the dataset", + "type": "string" + }, + "format": { + "description": "The dataset format, e.g. ARFF", + "type": "string" + } + }, + "type": "object" + }, + "EstimationProcedureList_estimationprocedures": { + "properties": { + "estimationprocedure": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EstimationProcedureList_estimationprocedures_estimationprocedure" + } + } + }, + "type": "object" + }, + "TaskTypeList": { + "properties": { + "task_types": { + "$ref": "#/components/schemas/TaskTypeList_task_types" + } + }, + "type": "object" + }, + "Run_run_description_output_data_evaluation": { + "properties": { + "array_data": { + "description": "For composite evaluation measures (e.g. per-class measures, confusion matrix), a string (JSON) representation of the evaluation.", + "type": "string" + }, + "name": { + "description": "The name of the evaluation measure", + "type": "string" + }, + "value": { + "description": "The result of the evaluation", + "type": "string" + }, + "flow_id": { + "description": "The id of the code used to compute this evaluation method", + "type": "string" + } + }, + "type": "object" + }, + "TaskList_task_quality": { + "properties": { + "name": { + "description": "The name of the quality", + "type": "string" + }, + "value": { + "description": "The value of the quality", + "type": "string" + } + }, + "type": "object" + }, + "Task_task_description": { + "properties": { + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Task_task_description_input" + } + }, + "task_type": { + "description": "The type of the task, e.g. Supervised Classification", + "type": "string" + }, + "tag": { + "description": "Tags added by OpenML uers. Includes study tags in the form 'study_1'", + "type": "array", + "items": { + "type": "string" + } + }, + "task_id": { + "description": "ID of the task, a positive integer", + "type": "string" + }, + "output": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Task_task_description_output" + } + } + }, + "type": "object" + }, + "inline_response_200_9_upload_flow": { + "properties": { + "id": { + "description": "ID of the uploaded flow, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "Flow_flow_description": { + "properties": { + "upload_date": { + "description": "The datetime that the flow was uploaded, format yyyy-MM-dd HH:mm:ss", + "type": "string" + }, + "description": { + "description": "Wiki description of the flow, in (Git flavoured) markdown format", + "type": "string" + }, + "language": { + "description": "The programming language the flow is written in.", + "type": "string" + }, + "parameter": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Flow_flow_description_parameter" + } + }, + "tag": { + "description": "Tags added by OpenML users. Includes study tags in the form `study_1`", + "type": "array", + "items": { + "type": "string" + } + }, + "version": { + "description": "The version of the flow, set by OpenML. A positive integer", + "type": "string" + }, + "version_label": { + "description": "The version of the flow, as defined by the uploader, for reference. Can be any format as long as it is unique.", + "type": "string" + }, + "dependencies": { + "description": "The libraries that this flow depends on, and their version numbers.", + "type": "string" + }, + "uploader": { + "description": "The uploader of the flow", + "type": "string" + }, + "id": { + "description": "ID of the flow, a positive integer", + "type": "string" + }, + "name": { + "description": "The name of the flow", + "type": "string" + } + }, + "type": "object" + }, + "Task": { + "properties": { + "task_description": { + "$ref": "#/components/schemas/Task_task_description" + } + }, + "type": "object" + }, + "Setup": { + "properties": { + "setup_parameters": { + "$ref": "#/components/schemas/Setup_setup_parameters" + } + }, + "type": "object" + }, + "FlowList_flows": { + "properties": { + "flow": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FlowList_flows_flow" + } + } + }, + "type": "object" + }, + "RunTrace": { + "properties": { + "trace": { + "$ref": "#/components/schemas/RunTrace_trace" + } + }, + "type": "object" + }, + "Task_task_description_predictions_feature": { + "properties": { + "type": { + "description": "The type of the prediction feature, e.g. integer", + "type": "string" + }, + "name": { + "description": "The name of the prediction feature, e.g. row_id", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_24_study_delete": { + "properties": { + "id": { + "description": "ID of the deleted study, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_5_upload_task": { + "properties": { + "id": { + "description": "ID of the uploaded task, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_13_flow_untag": { + "properties": { + "id": { + "description": "ID of the untagged flow", + "type": "string" + } + }, + "type": "object" + }, + "UserList_users": { + "properties": { + "user": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserList_users_user" + } + } + }, + "type": "object" + }, + "DataFeatures": { + "properties": { + "data_features": { + "$ref": "#/components/schemas/DataFeatures_data_features" + } + }, + "type": "object" + }, + "Task_task_description_predictions": { + "properties": { + "feature": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Task_task_description_predictions_feature" + } + }, + "format": { + "description": "The fromat of the predictions, e.g. ARFF", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_15_flow_tag": { + "properties": { + "id": { + "description": "ID of the tagged setup", + "type": "string" + } + }, + "type": "object" + }, + "Setup_setup_parameters": { + "properties": { + "parameter_setting": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Setup_setup_parameters_parameter_setting" + } + }, + "flow_id": { + "description": "ID of the flow, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "DataList": { + "properties": { + "data": { + "$ref": "#/components/schemas/DataList_data" + } + }, + "type": "object" + }, + "inline_response_200_26_study_attach": { + "properties": { + "linked_entities": { + "description": "The number of linked entities", + "type": "string" + }, + "main_entity_type": { + "description": "Main entity type of the of the study", + "type": "string" + }, + "id": { + "description": "ID of the study, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_2_data_tag": { + "properties": { + "id": { + "description": "ID of the tagged dataset", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_23_upload_flow": { + "properties": { + "id": { + "description": "ID of the run with the trace, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationRequest_evaluation_request": { + "properties": { + "run": { + "$ref": "#/components/schemas/EvaluationRequest_evaluation_request_run" + } + }, + "type": "object" + }, + "Run": { + "properties": { + "run_description": { + "$ref": "#/components/schemas/Run_run_description" + } + }, + "type": "object" + }, + "inline_response_200_6_task_tag": { + "properties": { + "id": { + "description": "ID of the tagged task", + "type": "string" + } + }, + "type": "object" + }, + "RunList": { + "properties": { + "runs": { + "$ref": "#/components/schemas/RunList_runs" + } + }, + "type": "object" + }, + "inline_response_200_17_data_delete": { + "properties": { + "id": { + "description": "ID of the deleted run, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "Task_task_description_input": { + "properties": { + "data_set": { + "$ref": "#/components/schemas/Task_task_description_data_set" + }, + "cost_matrix": { + "description": "The cost matrix, indicating the costs for each type of misclassification", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + } + }, + "name": { + "description": "The name of the input, e.g. source_data", + "type": "string" + }, + "evaluation_measures": { + "$ref": "#/components/schemas/Task_task_description_evaluation_measures" + }, + "estimation_procedure": { + "$ref": "#/components/schemas/Task_task_description_estimation_procedure" + } + }, + "type": "object" + }, + "UserList": { + "properties": { + "users": { + "$ref": "#/components/schemas/UserList_users" + } + }, + "type": "object" + }, + "Study_study_runs": { + "properties": { + "run_id": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "DataFeatures_data_features_feature": { + "properties": { + "index": { + "description": "Feature index", + "type": "string" + }, + "name": { + "description": "Feature name", + "type": "string" + }, + "data_type": { + "description": "Feature data type", + "type": "string" + }, + "is_target": { + "description": "Whether this feature is seen as a target feature", + "type": "string" + }, + "is_ignore": { + "description": "Whether this feature should be ignored in modelling (e.g. every value is unique)", + "type": "string" + }, + "is_row_identifier": { + "description": "Whether this feature is a row identifier", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_21_upload_flow": { + "properties": { + "id": { + "description": "ID of the evaluated run, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "TaskType": { + "properties": { + "description": { + "description": "A description of the task type", + "type": "string" + }, + "date": { + "description": "The date when the task type was created", + "type": "string" + }, + "output": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskType_output" + } + }, + "contributor": { + "type": "array", + "items": { + "type": "string" + } + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskType_input" + } + }, + "id": { + "description": "ID of the task type, a positive integer", + "type": "string" + }, + "name": { + "description": "The name of the task type, e.g. Supervised Classification", + "type": "string" + } + }, + "type": "object" + }, + "Run_run_description_output_data_file": { + "properties": { + "did": { + "description": "The id of the uploaded file", + "type": "string" + }, + "file_id": { + "description": "The reference id of the uploaded file, for downloading afterward", + "type": "string" + }, + "name": { + "description": "The name of the uploaded file (e.g., description, predictions, model,...)", + "type": "string" + } + }, + "type": "object" + }, + "TaskType_input": { + "properties": { + "data_set": { + "$ref": "#/components/schemas/Task_task_description_data_set" + }, + "cost_matrix": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + } + }, + "name": { + "description": "The name of the input, e.g. source_data", + "type": "string" + }, + "evaluation_measures": { + "$ref": "#/components/schemas/Task_task_description_evaluation_measures" + }, + "estimation_procedure": { + "$ref": "#/components/schemas/Task_task_description_estimation_procedure" + } + }, + "type": "object" + }, + "Run_run_description_output_data": { + "properties": { + "evaluation": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Run_run_description_output_data_evaluation" + } + }, + "file": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Run_run_description_output_data_file" + } + } + }, + "type": "object" + }, + "inline_response_200_3_data_untag": { + "properties": { + "id": { + "description": "ID of the untagged dataset", + "type": "string" + } + }, + "type": "object" + }, + "Task_task_description_output": { + "properties": { + "name": { + "description": "The name of the output, e.g. predictions", + "type": "string" + }, + "predictions": { + "$ref": "#/components/schemas/Task_task_description_predictions" + } + }, + "type": "object" + }, + "DataQualityList": { + "properties": { + "data_qualities_list": { + "$ref": "#/components/schemas/DataQualityList_data_qualities_list" + } + }, + "type": "object" + }, + "DataUnprocessed_data_unprocessed": { + "properties": { + "dataset": { + "$ref": "#/components/schemas/DataUnprocessed_data_unprocessed_dataset" + } + }, + "type": "object" + }, + "Study": { + "properties": { + "study": { + "$ref": "#/components/schemas/Study_study" + } + }, + "type": "object" + }, + "DataList_data": { + "properties": { + "dataset": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataList_data_dataset" + } + } + }, + "type": "object" + }, + "UserList_users_user": { + "properties": { + "username": { + "description": "The full user name", + "type": "string" + }, + "id": { + "description": "The user ID", + "type": "string" + } + }, + "type": "object" + }, + "TaskType_output": { + "properties": { + "name": { + "description": "The name of the output, e.g. predictions", + "type": "string" + }, + "predictions": { + "$ref": "#/components/schemas/TaskType_predictions" + } + }, + "type": "object" + }, + "RunList_runs_run": { + "properties": { + "task_id": { + "description": "The ID of the task solved by this run", + "type": "string" + }, + "run_id": { + "description": "The run ID", + "type": "string" + }, + "error_message": { + "description": "Error message generated by the run (if any)", + "type": "string" + }, + "setup_id": { + "description": "Ignore (internal representation of the parameter setting)", + "type": "string" + }, + "uploader": { + "description": "The ID of the person uploading this run", + "type": "string" + }, + "flow_id": { + "description": "The ID of the flow used in this run", + "type": "string" + } + }, + "type": "object" + }, + "DataFeatures_data_features": { + "properties": { + "feature": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataFeatures_data_features_feature" + } + } + }, + "type": "object" + }, + "DataQualities_data_qualities_quality": { + "properties": { + "name": { + "description": "The name of the dataset quality measures", + "type": "string" + }, + "value": { + "description": "The value for this dataset", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_12_flow_tag": { + "properties": { + "id": { + "description": "ID of the tagged flow", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_8_flow_delete": { + "properties": { + "id": { + "description": "ID of the deleted flow, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "DataQualities_data_qualities": { + "properties": { + "quality": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataQualities_data_qualities_quality" + } + } + }, + "type": "object" + }, + "EvaluationRequest": { + "properties": { + "evaluation_request": { + "$ref": "#/components/schemas/EvaluationRequest_evaluation_request" + } + }, + "type": "object" + }, + "inline_response_200_18_upload_flow": { + "properties": { + "id": { + "description": "ID of the uploaded run, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "SetupList": { + "properties": { + "setups": { + "$ref": "#/components/schemas/SetupList_setups" + } + }, + "type": "object" + }, + "TaskList": { + "properties": { + "task": { + "$ref": "#/components/schemas/TaskList_task" + } + }, + "type": "object" + }, + "inline_response_200_4_task_delete": { + "properties": { + "id": { + "description": "ID of the deleted task, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "Task_task_description_data_set": { + "properties": { + "data_set_id": { + "description": "The id of the dataset", + "type": "string" + }, + "target_feature": { + "description": "The name of the target feature for this task", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationList": { + "properties": { + "evaluations": { + "$ref": "#/components/schemas/EvaluationList_evaluations" + } + }, + "type": "object" + }, + "Run_run_description": { + "properties": { + "setup_string": { + "description": "Configuration of the flow as a string, to be interpreted by the flow, its library, or command line interface.", + "type": "string" + }, + "task_type": { + "description": "The type of task solved by this run (e.g., classification)", + "type": "string" + }, + "task_id": { + "description": "The id of the task solved by this run", + "type": "string" + }, + "task_evaluation_measure": { + "description": "The evaluation measure that is supposed to be optimized in the task, if any", + "type": "string" + }, + "uploader_name": { + "description": "The name of the uploader of the run", + "type": "string" + }, + "input_data": { + "$ref": "#/components/schemas/Run_run_description_input_data" + }, + "tag": { + "description": "Tags added by OpenML users. Includes study tags in the form `study_1`", + "type": "array", + "items": { + "type": "string" + } + }, + "output_data": { + "$ref": "#/components/schemas/Run_run_description_output_data" + }, + "uploader": { + "description": "The uploader of the run", + "type": "string" + }, + "flow_id": { + "description": "The id of the flow used in this run", + "type": "string" + }, + "flow_name": { + "description": "The name of the flow used in this run", + "type": "string" + }, + "id": { + "description": "ID of the run, a positive integer", + "type": "string" + }, + "parameter_setting": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Run_run_description_parameter_setting" + } + } + }, + "type": "object" + }, + "FlowList": { + "properties": { + "flows": { + "$ref": "#/components/schemas/FlowList_flows" + } + }, + "type": "object" + }, + "inline_response_200_19_run_tag": { + "properties": { + "id": { + "description": "ID of the tagged run", + "type": "string" + } + }, + "type": "object" + }, + "Study_study_data": { + "properties": { + "data_id": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "inline_response_200_data_delete": { + "properties": { + "id": { + "description": "ID of the deleted dataset, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "RunTrace_trace": { + "properties": { + "trace_iteration": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RunTrace_trace_trace_iteration" + } + }, + "run_id": { + "description": "run ID", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_7_task_untag": { + "properties": { + "id": { + "description": "ID of the untagged task", + "type": "string" + } + }, + "type": "object" + }, + "Data": { + "properties": { + "data_set_description": { + "$ref": "#/components/schemas/Data_data_set_description" + } + }, + "type": "object" + }, + "Task_task_description_evaluation_measures": { + "properties": { + "evaluation_measure": { + "description": "The evaluation measure to optimize in this task", + "type": "string" + } + }, + "type": "object" + }, + "SetupList_setups_parameter": { + "properties": { + "default_value": { + "description": "The parameter's default value", + "type": "string" + }, + "data_type": { + "description": "The parameter's data type", + "type": "string" + }, + "value": { + "description": "The parameter value in this setup", + "type": "string" + }, + "parameter_name": { + "description": "The parameter's short name", + "type": "string" + }, + "full_name": { + "description": "The parameter's full name", + "type": "string" + }, + "flow_id": { + "description": "The (sub)flow ID", + "type": "string" + }, + "flow_name": { + "description": "The (sub)flow name", + "type": "string" + }, + "id": { + "description": "The parameter ID", + "type": "string" + } + }, + "type": "object" + }, + "TaskList_task_input": { + "properties": { + "name": { + "description": "The name of the input", + "type": "string" + }, + "value": { + "description": "The value of the input", + "type": "string" + } + }, + "type": "object" + }, + "StudyList_study_list": { + "properties": { + "study": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StudyList_study_list_study" + } + } + }, + "type": "object" + }, + "Study_study_tag": { + "properties": { + "write_access": { + "description": "The write access level of the study (e.g. public)", + "type": "string" + }, + "name": { + "description": "The name of the study (e.g. study_1)", + "type": "string" + } + }, + "type": "object" + }, + "Flow": { + "properties": { + "flow_description": { + "$ref": "#/components/schemas/Flow_flow_description" + } + }, + "type": "object" + }, + "Run_run_description_parameter_setting": { + "properties": { + "name": { + "description": "The name of the parameter", + "type": "string" + }, + "value": { + "description": "The value of the parameter used", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationRequest_evaluation_request_run": { + "properties": { + "setup_id": { + "description": "ID of the setup, a positive integer", + "type": "string" + }, + "upload_time": { + "description": "The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss", + "type": "string" + }, + "uploader": { + "description": "ID of the uploader, a positive integer", + "type": "string" + }, + "task_id": { + "description": "ID of the task, a positive integer", + "type": "string" + }, + "run_id": { + "description": "ID of the run, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "Error": { + "properties": { + "message": { + "type": "string" + }, + "code": { + "type": "integer" + }, + "additional_message": { + "type": "string" + } + }, + "type": "object" + }, + "EstimationProcedureList_estimationprocedures_estimationprocedure": { + "properties": { + "name": { + "description": "The estimation procedure name, e.g. '10 fold Crossvalidation'", + "type": "string" + }, + "folds": { + "description": "The number of cross-validation folds, e.g. '10'", + "type": "string" + }, + "stratified_sampling": { + "description": "Whether or not the sampling is stratified, 'true' or 'false'", + "type": "string" + }, + "ttid": { + "description": "The task type ID", + "type": "string" + }, + "repeats": { + "description": "The number of repeats, e.g. '10'", + "type": "string" + }, + "type": { + "description": "The estimation procedure type, e.g. 'crossvalidation'", + "type": "string" + }, + "id": { + "description": "The estimation procedure ID", + "type": "string" + } + }, + "type": "object" + }, + "TaskTypeList_task_types": { + "properties": { + "task_type": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskTypeList_task_types_task_type" + } + } + }, + "type": "object" + }, + "TaskList_task_task": { + "properties": { + "status": { + "description": "The status of the source dataset, active, in_preparation, or deactivated", + "type": "string" + }, + "task_type": { + "description": "The type of task (e.g. Supervised Classificationr)", + "type": "string" + }, + "name": { + "description": "The name of the source dataset", + "type": "string" + }, + "task_id": { + "description": "The ID of the task", + "type": "string" + }, + "format": { + "description": "The format of the source dataset", + "type": "string" + }, + "did": { + "description": "The id of the source dataset", + "type": "string" + }, + "tag": { + "type": "array", + "items": { + "type": "string" + } + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskList_task_input" + } + }, + "quality": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskList_task_quality" + } + } + }, + "type": "object" + }, + "inline_response_200_20_run_untag": { + "properties": { + "id": { + "description": "ID of the untagged run", + "type": "string" + } + }, + "type": "object" + }, + "StudyList": { + "properties": { + "study_list": { + "$ref": "#/components/schemas/StudyList_study_list" + } + }, + "type": "object" + }, + "Study_study": { + "properties": { + "runs": { + "$ref": "#/components/schemas/Study_study_runs" + }, + "tasks": { + "$ref": "#/components/schemas/Study_study_tasks" + }, + "name": { + "description": "The name of the study", + "type": "string" + }, + "creator": { + "description": "A comma-separated list of the study creators", + "type": "string" + }, + "flows": { + "$ref": "#/components/schemas/Study_study_flows" + }, + "creation_date": { + "description": "The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss", + "type": "string" + }, + "alias": { + "description": "The alias of the study", + "type": "string" + }, + "tag": { + "$ref": "#/components/schemas/Study_study_tag" + }, + "main_entity_type": { + "description": "The type of entity collected in the study (e.g. task or run)", + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/Study_study_data" + }, + "id": { + "description": "The ID of the study", + "type": "string" + } + }, + "type": "object" + }, + "inline_response_200_25_upload_study": { + "properties": { + "id": { + "description": "ID of the uploaded study, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationList_evaluations_evaluation": { + "properties": { + "function": { + "description": "The name of the evaluation function", + "type": "string" + }, + "task_id": { + "description": "The ID of the tasks solved by this run", + "type": "string" + }, + "run_id": { + "description": "The run ID", + "type": "string" + }, + "array_data": { + "description": "For structured evaluation measures, an array of evaluation values (e.g. per-class predictions, evaluation matrices,...)", + "type": "string" + }, + "value": { + "description": "The outcome of the evaluation", + "type": "string" + }, + "flow_id": { + "description": "The ID of the flow used by this run", + "type": "string" + } + }, + "type": "object" + }, + "EstimationProcedureList": { + "properties": { + "estimationprocedures": { + "$ref": "#/components/schemas/EstimationProcedureList_estimationprocedures" + } + }, + "type": "object" + }, + "DataQualityList_data_qualities_list": { + "properties": { + "quality": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "Study_study_flows": { + "properties": { + "flow_id": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "StudyList_study_list_study": { + "properties": { + "alias": { + "description": "The alias of the study", + "type": "string" + }, + "creation_date": { + "description": "The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss", + "type": "string" + }, + "creator": { + "description": "A comma-separated list of the study creators", + "type": "string" + }, + "id": { + "description": "The ID of the study", + "type": "string" + }, + "name": { + "description": "The name of the study", + "type": "string" + } + }, + "type": "object" + }, + "Run_run_description_input_data": { + "properties": { + "dataset": { + "$ref": "#/components/schemas/Run_run_description_input_data_dataset" + } + }, + "type": "object" + }, + "TaskTypeList_task_types_task_type": { + "properties": { + "description": { + "description": "A description of the task type", + "type": "string" + }, + "creator": { + "description": "A comma-separated list of the task type creators", + "type": "string" + }, + "id": { + "description": "The ID of the task type", + "type": "string" + }, + "name": { + "description": "The name of the task type", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationMeasureList_evaluation_measures": { + "properties": { + "measures": { + "$ref": "#/components/schemas/EvaluationMeasureList_evaluation_measures_measures" + } + }, + "type": "object" + }, + "TaskType_predictions": { + "properties": { + "feature": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Task_task_description_predictions_feature" + } + }, + "format": { + "description": "The format of the predictions, e.g. ARFF", + "type": "string" + } + }, + "type": "object" + }, + "Flow_flow_description_parameter": { + "properties": { + "default_value": { + "description": "The default value of the parameter", + "type": "string" + }, + "name": { + "description": "The name of the parameter", + "type": "string" + }, + "data_type": { + "description": "The data type of the parameter", + "type": "string" + }, + "description": { + "description": "A description of the parameter", + "type": "string" + } + }, + "type": "object" + }, + "RunTrace_trace_trace_iteration": { + "properties": { + "setup_string": { + "description": "A JSON representation of the setup (configuration)", + "type": "string" + }, + "repeat": { + "description": "The number of the repeat in the outer cross-valudation", + "type": "string" + }, + "selected": { + "description": "Whether this setup was selected as the best one (true or false)", + "type": "string" + }, + "iteration": { + "description": "A number of the optimization iteration", + "type": "string" + }, + "fold": { + "description": "The number of the fold in the inner cross-validation", + "type": "string" + }, + "evaluation": { + "description": "The evaluation score of the setup", + "type": "string" + } + }, + "type": "object" + }, + "EvaluationMeasureList": { + "properties": { + "evaluation_measures": { + "$ref": "#/components/schemas/EvaluationMeasureList_evaluation_measures" + } + }, + "type": "object" + }, + "SetupList_setups": { + "properties": { + "setup": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SetupList_setups_setup" + } + } + }, + "type": "object" + }, + "TaskList_task": { + "properties": { + "task": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TaskList_task_task" + } + } + }, + "type": "object" + }, + "SetupList_setups_setup": { + "properties": { + "setup_id": { + "description": "The setup ID", + "type": "string" + }, + "parameter": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SetupList_setups_parameter" + } + }, + "flow_id": { + "description": "The ID of the flow used by this run", + "type": "string" + } + }, + "type": "object" + }, + "Task_task_description_estimation_procedure_parameter": { + "properties": { + "name": { + "description": "The name of the parameter", + "type": "string" + }, + "value": { + "description": "The value of the parameter", + "type": "string" + } + }, + "type": "object" + }, + "RunList_runs": { + "properties": { + "run": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RunList_runs_run" + } + } + }, + "type": "object" + }, + "Study_study_tasks": { + "properties": { + "task_id": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "type": "object" + }, + "inline_response_200_1_upload_data_set": { + "properties": { + "id": { + "description": "ID of the uploaded dataset, a positive integer", + "type": "string" + } + }, + "type": "object" + }, + "Setup_setup_parameters_parameter_setting": { + "properties": { + "default_value": { + "description": "The default value of the parameter used", + "type": "string" + }, + "value": { + "description": "The value of the parameter used", + "type": "string" + }, + "data_type": { + "description": "The data type of the hyperparameter value", + "type": "string" + }, + "full_name": { + "description": "The full name of the hyperparameter", + "type": "string" + }, + "parameter_name": { + "description": "The short name of the hyperparameter", + "type": "string" + } + }, + "type": "object" + }, + "DataQualities": { + "properties": { + "data_qualities": { + "$ref": "#/components/schemas/DataQualities_data_qualities" + } + }, + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/openapi/swagger.yaml b/openapi/swagger.yaml new file mode 100644 index 000000000..3c86cfc16 --- /dev/null +++ b/openapi/swagger.yaml @@ -0,0 +1,3338 @@ +openapi: 3.0.0 +info: + title: 'OpenML API' + description: 'REST API for sharing, organizing and reusing machine learning datasets, code, and experiments. Follows a predictive URL scheme from endpoint https://www.openml.org/api/v1/json (or /xml). You need to add your api_key for every call except GET calls. The API key can be found in your profile on openml.org.' + version: 1.0.0 +servers: + - + url: 'https://openml.org/api/v1/json' + description: 'The current OpenML API' + - + url: 'https://test.openml.org/api/v1/json' + description: 'The test OpenML API' +paths: + /data/tag: + post: + tags: + - data + summary: 'Tag a dataset' + description: 'Tags a dataset.' + operationId: 'Api_data::data_tag' + parameters: + - + name: data_id + in: query + description: 'Id of the dataset.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the tagged dataset' + content: + application/json: + schema: + properties: + data_tag: { $ref: '#/components/schemas/inline_response_200_2_data_tag' } + type: object + example: + data_tag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /data/untag: + post: + tags: + - data + summary: 'Untag a dataset' + description: 'Untags a dataset.' + operationId: 'Api_data::data_untag' + parameters: + - + name: data_id + in: query + description: 'Id of the dataset.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The ID of the untagged dataset' + content: + application/json: + schema: + properties: + data_untag: { $ref: '#/components/schemas/inline_response_200_3_data_untag' } + type: object + example: + data_untag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/data/list/{filters}': + get: + tags: + - data + summary: 'List and filter datasets' + description: 'List datasets, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form ''/data/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all datasets that match the constraints.' + operationId: 'Api_data::data_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n /status/{status} - returns only datasets with a given status, either 'active', 'deactivated', or 'in_preparation'.\r\n /tag/{tag} - returns only datasets tagged with the given tag.\r\n /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, data_version, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'.\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of datasets with the given task' + content: + application/json: + schema: + $ref: '#/components/schemas/DataList' + example: + data: + dataset: [{ did: '1', name: anneal, status: active, format: ARFF, quality: [{ name: MajorityClassSize, value: '684' }, { name: MaxNominalAttDistinctValues, value: '10.0' }, { name: MinorityClassSize, value: '0' }, { name: NumBinaryAtts, value: '14.0' }, { name: NumberOfClasses, value: '6' }, { name: NumberOfFeatures, value: '39' }, { name: NumberOfInstances, value: '898' }, { name: NumberOfInstancesWithMissingValues, value: '0' }, { name: NumberOfMissingValues, value: '0' }, { name: NumberOfNumericFeatures, value: '6' }, { name: NumberOfSymbolicFeatures, value: '32' }] }] + '412': + description: 'Precondition failed. An error code and message are returned.\n370 - Illegal filter specified.\n371 - Filter values/ranges not properly specified.\n372 - No results. There where no matches for the given constraints.\n373 - Can not specify an offset without a limit.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/data/{id}': + get: + tags: + - data + summary: 'Get dataset description' + description: 'Returns information about a dataset. The information includes the name, information about the creator, URL to download it and more.' + operationId: 'Api_data::data' + parameters: + - + name: id + in: path + description: 'Id of the dataset.' + required: true + schema: + type: integer + responses: + '200': + description: 'A dataset description' + content: + application/json: + schema: + $ref: '#/components/schemas/Data' + example: + data_set_description: + id: '1' + name: anneal + version: '2' + description: ... + format: ARFF + upload_date: '2014-04-06 23:19:20' + licence: Public + url: 'https://www.openml.org/data/download/1/dataset_1_anneal.arff' + file_id: '1' + default_target_attribute: class + version_label: '2' + tag: [study_1, uci] + visibility: public + original_data_url: 'https://www.openml.org/d/2' + status: active + md5_checksum: d01f6ccd68c88b749b20bbe897de3713 + '412': + description: 'Precondition failed. An error code and message are returned\n110 - Please provide data_id.\n111 - Unknown dataset. Data set description with data_id was not found in the database.\n112 - No access granted. This dataset is not shared with you.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - data + summary: 'Delete dataset' + description: 'Deletes a dataset. Upon success, it returns the ID of the deleted dataset.' + operationId: 'Api_data::data_delete' + parameters: + - + name: id + in: path + description: 'Id of the dataset.' + required: true + schema: + type: integer + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'ID of the deleted dataset' + content: + application/json: + schema: + properties: + data_delete: { $ref: '#/components/schemas/inline_response_200_data_delete' } + type: object + example: + data_delete: + id: '4328' + '412': + description: 'Precondition failed. An error code and message are returned\n- 350 - Please provide API key. In order to remove your content, please authenticate.\n- 351 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n- 352 - Dataset does not exists. The data ID could not be linked to an existing dataset.\n- 353 - Dataset is not owned by you. The dataset is owned by another user. Hence you cannot delete it.\n- 354 - Dataset is in use by other content. Can not be deleted. The data is used in tasks or runs. Delete other content before deleting this dataset.\n- 355 - Deleting dataset failed. Deleting the dataset failed. Please contact support team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /data: + post: + tags: + - data + summary: 'Upload dataset' + description: 'Uploads a dataset. Upon success, it returns the data id.' + operationId: 'Api_data::data_upload' + parameters: + - + name: description + in: query + description: 'An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/data).' + required: true + schema: + type: file + - + name: dataset + in: query + description: 'The actual dataset, being an ARFF file.' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the uploaded dataset' + content: + application/json: + schema: + properties: + upload_data_set: { $ref: '#/components/schemas/inline_response_200_1_upload_data_set' } + type: object + example: + upload_data_set: + id: '4328' + '412': + description: 'Precondition failed. An error code and message are returned.\n130 - Problem with file uploading. There was a problem with the file upload.\n131 - Problem validating uploaded description file. The XML description format does not meet the standards.\n132 - Failed to move the files. Internal server error, please contact API administrators.\n133 - Failed to make checksum of datafile. Internal server error, please contact API administrators.\n134 - Failed to insert record in database. Internal server error, please contact API administrators.\n135 - Please provide description xml.\n136 - File failed format verification. The uploaded file is not valid according to the selected file format. Please check the file format specification and try again.\n137 - Please provide API key. In order to share content, please log in or provide your API key.\n138 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n139 - Combination name / version already exists. Leave version out for auto increment\n140 - Both dataset file and dataset url provided. The system is confused since both a dataset file (post) and a dataset url (xml) are provided. Please remove one.\n141 - Neither dataset file or dataset url are provided. Please provide either a dataset file as POST variable, or a dataset url in the description XML.\n142 - Error in processing arff file. Can be a syntax error, or the specified target feature does not exists. For now, we only check on arff files. If a dataset is claimed to be in such a format, and it can not be parsed, this error is returned.\n143 - Suggested target feature not legal. It is possible to suggest a default target feature (for predictive tasks). However, it should be provided in the data.\n144 - Unable to update dataset. The dataset with id could not be found in the database. If you upload a new dataset, unset the id.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /data/status/update/: + post: + tags: + - data + summary: 'Change the status of a dataset' + description: 'Change the status of a dataset, either ''active'' or ''deactivated''' + operationId: 'Api_data::status_update' + parameters: + - + name: data_id + in: query + description: 'Id of the dataset.' + required: true + schema: + type: integer + - + name: status + in: query + description: 'The status on which to filter the results, either ''active'' or ''deactivated''.' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '412': + description: 'Precondition failed. An error code and message are returned.\n691 - Illegal status\n692 - Dataset does not exists\n693 - Dataset is not owned by you\n694 - Illegal status transition\n695 - Status update failed\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/data/features/{id}': + get: + tags: + - data + summary: 'Get data features' + description: 'Returns the features of a dataset.' + operationId: 'Api_data::data_features' + parameters: + - + name: id + in: path + description: 'Id of the dataset.' + required: true + schema: + type: integer + responses: + '200': + description: 'All the features of the dataset' + content: + application/json: + schema: + $ref: '#/components/schemas/DataFeatures' + example: + data_features: + feature: [{ index: '0', name: sepallength, data_type: numeric, is_target: 'false', is_ignore: 'false', is_row_identifier: 'false' }, { index: '1', name: sepalwidth, data_type: numeric, is_target: 'false', is_ignore: 'false', is_row_identifier: 'false' }, { index: '2', name: petallength, data_type: numeric, is_target: 'false', is_ignore: 'false', is_row_identifier: 'false' }, { index: '3', name: petalwidth, data_type: numeric, is_target: 'false', is_ignore: 'false', is_row_identifier: 'false' }, { index: '4', name: class, data_type: nominal, is_target: 'true', is_ignore: 'false', is_row_identifier: 'false' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n270 - Please provide dataset ID.\n271 - Unknown dataset. Data set with the given data ID was not found (or is not shared with you).\n272 - No features found. The dataset did not contain any features, or we could not extract them.\n273 - Dataset not processed yet. The dataset was not processed yet, features are not yet available. Please wait for a few minutes.\n274 - Dataset processed with error. The feature extractor has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, please contact the API admins.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /data/features: + post: + tags: + - data + summary: 'Upload dataset feature description' + description: 'Uploads dataset feature description. Upon success, it returns the data id.' + operationId: 'Api_data::data_features_upload' + parameters: + - + name: description + in: query + description: 'An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.features) and an [XML example](https://www.openml.org/api/v1/xml_example/data.features).' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '412': + description: 'Precondition failed. An error code and message are returned.\n431 - Dataset already processed\n432 - Please provide description xml\n433 - Problem validating uploaded description file\n434 - Could not find dataset\n436 - Something wrong with XML, check data id and evaluation engine id\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /data/qualities/list: + get: + tags: + - data + summary: 'List all data qualities' + description: 'Returns a list of all data qualities in the system.' + operationId: 'Api_data::data_qualities_list' + responses: + '200': + description: 'A list of data qualities' + content: + application/json: + schema: + $ref: '#/components/schemas/DataQualityList' + example: + data_qualities_list: + quality: [NumberOfClasses, NumberOfFeatures, NumberOfInstances, NumberOfInstancesWithMissingValues, NumberOfMissingValues, NumberOfNumericFeatures, NumberOfSymbolicFeatures] + '412': + description: 'Precondition failed. An error code and message are returned\n370 - No data qualities available. There are no data qualities in the system.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/data/qualities/{id}': + get: + tags: + - data + summary: 'Get data qualities' + description: 'Returns the qualities of a dataset.' + operationId: 'Api_data::data_qualities' + parameters: + - + name: id + in: path + description: 'Id of the dataset.' + required: true + schema: + type: integer + responses: + '200': + description: 'All the qualities of the dataset' + content: + application/json: + schema: + $ref: '#/components/schemas/DataQualities' + example: + data_qualities: + quality: [{ name: ClassCount, value: '3.0' }, { name: ClassEntropy, value: '1.584962500721156' }, { name: NumberOfClasses, value: '3' }, { name: NumberOfFeatures, value: '5' }, { name: NumberOfInstances, value: '150' }, { name: NumberOfInstancesWithMissingValues, value: '0' }, { name: NumberOfMissingValues, value: '0' }, { name: NumberOfNumericFeatures, value: '4' }, { name: NumberOfSymbolicFeatures, value: '0' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n360 - Please provide data set ID\n361 - Unknown dataset. The data set with the given ID was not found in the database, or is not shared with you.\n362 - No qualities found. The registered dataset did not contain any calculated qualities.\n363 - Dataset not processed yet. The dataset was not processed yet, no qualities are available. Please wait for a few minutes.\n364 - Dataset processed with error. The quality calculator has run into an error while processing the dataset. Please check whether it is a valid supported file. If so, contact the support team.\n365 - Interval start or end illegal. There was a problem with the interval\nstart or end.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /data/qualities: + post: + tags: + - data + summary: 'Upload dataset qualities' + description: 'Uploads dataset qualities. Upon success, it returns the data id.' + operationId: 'Api_data::data_qualities_upload' + parameters: + - + name: description + in: query + description: 'An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.data.qualities) and an [XML example](https://www.openml.org/api/v1/xml_example/data.qualities).' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '412': + description: 'Precondition failed. An error code and message are returned.\n381 - Something wrong with XML, please check did and evaluation_engine_id\n382 - Please provide description xml\n383 - Problem validating uploaded description file\n384 - Dataset not processed yet\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/data/unprocessed/{data_engine_id}/{order}': + get: + tags: + - data + summary: 'Get a list of unprocessed datasets' + description: 'This call is for people running their own dataset processing engines. It returns the details of datasets that are not yet processed by the given processing engine. It doesn''t process the datasets, it just returns the dataset info.' + operationId: 'Api_data::data_unprocessed' + parameters: + - + name: data_engine_id + in: path + description: 'The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.' + required: true + schema: + type: integer + - + name: order + in: path + description: 'When there are multiple datasets still to process, this defines which ones to return. Options are ''normal'' - the oldest datasets, or ''random''.' + required: true + schema: + type: string + responses: + '200': + description: 'A list of unprocessed datasets' + content: + application/json: + schema: + $ref: '#/components/schemas/DataUnprocessed' + example: + data_unprocessed: + run: [{ did: '1', status: deactivated, version: '2', name: anneal, format: ARFF }] + '412': + description: 'Precondition failed. An error code and message are returned.\n681 - No unprocessed datasets.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/data/qualities/unprocessed/{data_engine_id}/{order}': + post: + tags: + - data + summary: 'Get a list of datasets with unprocessed qualities' + description: 'This call is for people running their own dataset processing engines. It returns the details of datasets for which certain qualities are not yet processed by the given processing engine. It doesn''t process the datasets, it just returns the dataset info.' + operationId: 'Api_data::dataqualities_unprocessed' + parameters: + - + name: data_engine_id + in: path + description: 'The ID of the data processing engine. You get this ID when you register a new data processing engine with OpenML. The ID of the main data processing engine is 1.' + required: true + schema: + type: string + - + name: order + in: path + description: 'When there are multiple datasets still to process, this defines which ones to return. Options are ''normal'' - the oldest datasets, or ''random''.' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'API key to authenticate the user' + required: false + schema: + type: string + - + name: qualities + in: query + description: 'Comma-separated list of (at least two) quality names, e.g. ''NumberOfInstances,NumberOfFeatures''.' + required: true + schema: + type: string + responses: + '200': + description: 'A list of unprocessed datasets' + content: + application/json: + schema: + $ref: '#/components/schemas/DataUnprocessed' + example: + data_unprocessed: + run: [{ did: '1', status: deactivated, version: '2', name: anneal, format: ARFF }] + '412': + description: 'Precondition failed. An error code and message are returned.\n686 - Please specify the features the evaluation engine wants to calculate (at least 2).\n687 - No unprocessed datasets according to the given set of meta-features.\n688 - Illegal qualities.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /estimationprocedure/list: + get: + tags: + - estimationprocedure + summary: 'List all estimation procedures' + description: 'Returns an array with all model performance estimation procedures in the system.' + operationId: 'Api_estimationprocedure::estimationprocedure_list' + responses: + '200': + description: 'A list of estimation procedures' + content: + application/json: + schema: + $ref: '#/components/schemas/EstimationProcedureList' + example: + estimationprocedures: + estimationprocedure: [{ id: '1', ttid: '1', name: '10-fold Crossvalidation', type: crossvalidation, repeats: '1', folds: '10', stratified_sampling: 'true' }, { id: '2', ttid: '1', name: '5 times 2-fold Crossvalidation', type: crossvalidation, repeats: '5', folds: '2', stratified_sampling: 'true' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n500 - No model performance estimation procedures available.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/evaluation/request/{evaluation_engine_id}/{order}': + get: + tags: + - evaluation + summary: 'Get an unevaluated run' + description: 'This call is for people running their own evaluation engines. It returns the details of a run that is not yet evaluated by the given evaluation engine. It doesn''t evaluate the run, it just returns the run info.' + operationId: 'Api_evaluation::evaluation_request' + parameters: + - + name: evaluation_engine_id + in: path + description: 'The ID of the evaluation engine. You get this ID when you register a new evaluation engine with OpenML. The ID of the main evaluation engine is 1.' + required: true + schema: + type: string + - + name: order + in: path + description: 'When there are multiple runs still to evaluate, this defines which one to return. Options are ''normal'' - the oldest run, ''reverse'' - the newest run, or ''random'' - a random run.' + required: true + schema: + type: string + responses: + '200': + description: 'A list of evaluations descriptions' + content: + application/json: + schema: + $ref: '#/components/schemas/EvaluationRequest' + example: + evaluation_request: + run: [{ setup_id: '68799271', upload_time: '2018-04-03 21:05:38', uploader: '1935', task_id: '3021', run_id: '8943712' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n100 - Function not valid.\n545 - No unevaluated runs according to the criteria.\n546 - Illegal filter.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/evaluation/list/{filters}': + get: + tags: + - evaluation + summary: 'List and filter evaluations' + description: 'List evaluations, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form ''/evaluation/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all evaluations that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, uploaders or runs.' + operationId: 'Api_evaluation::evaluation_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /function/{name} - name of the evaluation measure, e.g. area_under_auc or predictive_accuracy. See the OpenML website for the complete list of measures.\r\n /tag/{tag} - returns only evaluations of runs tagged with the given tag.\r\n /run/{ids} - return only evaluations for specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3''\r\n /task/{ids} - return only evaluations for specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3''\r\n /flow/{ids} - return only evaluations for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3''\r\n /setup/{ids} - return only evaluations for specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3''\r\n /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n /per_fold/{true,false} - whether or not to return crossvalidation scores per fold. Defaults to 'false'. Setting it to 'true' leads to large numbers of results, use only for very specific sets of runs.\r\n /sort_order/{asc,desc} - sorts the results by the evaluation value, according to the selected evaluation measure (function)\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of evaluations descriptions' + content: + application/json: + schema: + $ref: '#/components/schemas/EvaluationList' + example: + evaluations: + evaluation: [{ function: area_under_roc_curve, upload_time: '2014-04-06 23:30:40', task_id: '68', run_id: '1', array_data: '[0,0.99113,0.898048,0.874862,0.791282,0.807343,0.820674]', value: '0.839359', uploader: '1', flow_id: '61' }, { function: f_measure, upload_time: '2014-04-06 23:30:40', task_id: '68', run_id: '1', array_data: '[0,0,0.711934,0.735714,0.601363,0.435678,0.430913]', value: '0.600026', uploader: '1', flow_id: '61' }, { function: predictive_accuracy, upload_time: '2014-04-06 23:30:40', task_id: '68', run_id: '1', array_data: [], value: '0.614634', uploader: '1', flow_id: '61' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n540 - Please provide at least task, flow or setup, uploader or run, to\nfilter results, or limit the number of responses.\n541 - The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n542 - There where no results. Check whether there are runs under the given constraint.\n543 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n544 - Illegal filter specified.\n545 - Offset specified without limit.\n546 - Requested result limit too high.\n547 - Per fold can only be set to value ''true'' or ''false''.\n548 - Per fold queries are experimental and require a fair amount of filters on resulting run records to keep the query fast (use, e.g., flow, setup, task and uploader filter)\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /evaluationmeasure/list: + get: + tags: + - evaluationmeasure + summary: 'List all evaluation measures' + description: 'Returns an array with all model evaluation measures in the system.' + operationId: 'Api_evaluationmeasure::evaluationmeasure_list' + responses: + '200': + description: 'A list of evaluation measures' + content: + application/json: + schema: + $ref: '#/components/schemas/EvaluationMeasureList' + example: + evaluation_measures: + measures: { measure: [area_under_roc_curve, average_cost, binominal_test, build_cpu_time] } + '/flow/list/{filters}': + get: + tags: + - flow + summary: 'List and filter flows' + description: 'List flows, possibly filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form ''/task/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all flows that match the constraints.' + operationId: 'Api_flow::flow_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified.\r\n /tag/{tag} - returns only tasks tagged with the given tag.\r\n /uploader/{id} - return only evaluations uploaded by a specific user, specified by user ID.\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of flows' + content: + application/json: + schema: + $ref: '#/components/schemas/FlowList' + example: + flows: + flow: [{ id: '65', full_name: weka.RandomForest(1), name: weka.RandomForest, version: '1', external_version: Weka_3.7.10_9186, uploader: '1' }, { id: '66', full_name: weka.IBk(1), name: weka.IBk, version: '1', external_version: Weka_3.7.10_8034, uploader: '1' }, { id: '67', full_name: weka.BayesNet_K2(1), name: weka.BayesNet_K2, version: '1', external_version: Weka_3.7.10_8034, uploader: '1' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n500 - No results. There where no matches for the given constraints.\n501 - Illegal filter specified.\n502 - Filter values/ranges not properly specified.\n503 - Can not specify an offset without a limit.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /flow/tag: + post: + tags: + - flow + summary: 'Tag a flow' + description: 'Tags a flow.' + operationId: 'Api_flow::flow_tag' + parameters: + - + name: flow_id + in: query + description: 'Id of the flow.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the tagged flow' + content: + application/json: + schema: + properties: + flow_tag: { $ref: '#/components/schemas/inline_response_200_12_flow_tag' } + type: object + example: + flow_tag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /flow/untag: + post: + tags: + - flow + summary: 'Untag a flow' + description: 'Untags a flow.' + operationId: 'Api_flow::flow_untag' + parameters: + - + name: flow_id + in: query + description: 'Id of the flow.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the untagged flow' + content: + application/json: + schema: + properties: + flow_untag: { $ref: '#/components/schemas/inline_response_200_13_flow_untag' } + type: object + example: + flow_untag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/flow/exists/{name}/{version}': + get: + tags: + - flow + summary: 'Check whether flow exists' + description: 'Checks whether a flow with the given name and (external) version exists.' + operationId: 'Api_flow::flow_exists' + parameters: + - + name: name + in: path + description: 'The name of the flow.' + required: true + schema: + type: string + - + name: version + in: path + description: 'The external version of the flow' + required: true + schema: + type: string + responses: + '200': + description: 'A list of flows' + content: + application/json: + schema: + properties: + flow_exists: { $ref: '#/components/schemas/inline_response_200_10_flow_exists' } + type: object + example: + flow_exists: + exists: 'true' + id: '65' + '412': + description: 'Precondition failed. An error code and message are returned.\n330 - Mandatory fields not present. Please provide name and external_version.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/flow/{id}': + get: + tags: + - flow + summary: 'Get flow description' + description: 'Returns information about a flow. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.' + operationId: 'Api_flow::flow' + parameters: + - + name: id + in: path + description: 'ID of the flow.' + required: true + schema: + type: integer + responses: + '200': + description: 'A flow description' + content: + application/json: + schema: + $ref: '#/components/schemas/Flow' + example: + flow: + id: '100' + uploader: '1' + name: weka.J48 + version: '2' + external_version: Weka_3.7.5_9117 + description: ... + upload_date: '2014-04-23 18:00:36' + language: Java + dependencies: Weka_3.7.5 + parameter: [{ name: A, data_type: flag, default_value: [], description: 'Laplace smoothing...' }, { name: C, data_type: option, default_value: '0.25', description: 'Set confidence threshold...' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n180 - Please provide flow id.\n181 - Unknown flow. The flow with the given ID was not found in the database.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - flow + summary: 'Delete a flow' + description: 'Deletes a flow. Upon success, it returns the ID of the deleted flow.' + operationId: 'Api_flow::flow_delete' + parameters: + - + name: id + in: path + description: 'Id of the flow.' + required: true + schema: + type: integer + - + name: api_key + in: query + description: 'API key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'ID of the deleted flow' + content: + application/json: + schema: + properties: + flow_delete: { $ref: '#/components/schemas/inline_response_200_8_flow_delete' } + type: object + example: + flow_delete: + id: '4328' + '412': + description: 'Precondition failed. An error code and message are returned.\n320 - Please provide API key. In order to remove your content, please authenticate.\n321 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n322 - Flow does not exists. The flow ID could not be linked to an existing flow.\n323 - Flow is not owned by you. The flow is owned by another user. Hence you cannot delete it.\n324 - Flow is in use by other content. Can not be deleted. The flow is used in runs, evaluations or as a component of another flow. Delete other content before deleting this flow.\n325 - Deleting flow failed. Deleting the flow failed. Please contact\nsupport team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /flow: + post: + tags: + - flow + summary: 'Upload a flow' + description: 'Uploads a flow. Upon success, it returns the flow id.' + operationId: 'Api_flow::flow_upload' + parameters: + - + name: description + in: query + description: 'An XML file describing the flow. Only name and description are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.implementation.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/flow).' + required: true + schema: + type: file + - + name: flow + in: query + description: 'The actual flow, being a source (or binary) file.' + required: false + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the uploaded flow' + content: + application/json: + schema: + properties: + upload_flow: { $ref: '#/components/schemas/inline_response_200_9_upload_flow' } + type: object + example: + upload_flow: + id: '2520' + '412': + description: 'Precondition failed. An error code and message are returned.\n160 - Error in file uploading. There was a problem with the file upload.\n161 - Please provide description xml.\n163 - Problem validating uploaded description file. The XML description format does not meet the standards.\n164 - Flow already stored in database. Please change name or version number\n165 - Failed to insert flow. There can be many causes for this error. If you included the implements field, it should be an existing entry in the algorithm or math_function table. Otherwise it could be an internal server error. Please contact API support team.\n166 - Failed to add flow to database. Internal server error, please contact API administrators\n167 - Illegal files uploaded. An non required file was uploaded.\n168 - The provided md5 hash equals not the server generated md5 hash of the file.\n169 - Please provide API key. In order to share content, please authenticate and provide API key.\n170 - Authentication failed. The API key was not valid. Please try to login again, or contact API administrators\n171 - Flow already exists. This flow is already in the database\n172 - XSD not found. Please contact API support team\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /run/tag: + post: + tags: + - run + summary: 'Tag a run' + description: 'Tags a run.' + operationId: 'Api_run::run_tag' + parameters: + - + name: run_id + in: query + description: 'Id of the run.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the tagged run' + content: + application/json: + schema: + properties: + run_tag: { $ref: '#/components/schemas/inline_response_200_19_run_tag' } + type: object + example: + run_tag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /run/untag: + post: + tags: + - run + summary: 'Untag a run' + description: 'Untags a run.' + operationId: 'Api_run::run_untag' + parameters: + - + name: run_id + in: query + description: 'Id of the run.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the untagged run' + content: + application/json: + schema: + properties: + run_untag: { $ref: '#/components/schemas/inline_response_200_20_run_untag' } + type: object + example: + run_untag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/run/list/{filters}': + get: + tags: + - run + summary: 'List and filter runs' + description: 'List runs, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form ''/run/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all runs that match the constraints. A maximum of 10,000 results are returned, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain tasks, flows, setups, or uploaders.' + operationId: 'Api_run::run_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /tag/{tag} - return only runs tagged with the given tag.\r\n /run/{ids} - return only specific runs, specified as a comma-separated list of run IDs, e.g. ''1,2,3''\r\n /task/{ids} - return only runs on specific tasks, specified as a comma-separated list of task IDs, e.g. ''1,2,3''\r\n /flow/{ids} - return only runs on specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3''\r\n /setup/{ids} - return only runs with specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3''\r\n /uploader/{ids} - return only runs uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of runs descriptions' + content: + application/json: + schema: + $ref: '#/components/schemas/RunList' + example: + runs: + run: [{ upload_time: '2014-04-06 23:30:40', task_id: '28', run_id: '100', error_message: [], setup_id: '12', uploader: '1', flow_id: '67' }, { upload_time: '2014-04-06 23:30:40', task_id: '48', run_id: '101', error_message: [], setup_id: '6', uploader: '1', flow_id: '61' }, { upload_time: '2014-04-06 23:30:40', task_id: '41', run_id: '102', error_message: [], setup_id: '3', uploader: '1', flow_id: '58' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n510 - Please provide at least task, flow or setup, uploader or run, to filter results, or limit the number of responses. The number of runs is huge. Please limit the result space.\n511 - Input not safe. The input parameters (task_id, setup_id, flow_id, run_id, uploader_id) did not meet the constraints (comma separated list of integers).\n512 - There where no results. Check whether there are runs under the given constraint.\n513 - Too many results. Given the constraints, there were still too many results. Please add filters to narrow down the list.\n514 - Illegal filter specified.\n515 - Offset specified without limit.\n516 - Requested result limit too high.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/run/{id}': + get: + tags: + - run + summary: 'Get run description' + description: 'Returns information about a run. The information includes the name, information about the creator, dependencies, parameters, run instructions and more.' + operationId: 'Api_run::run' + parameters: + - + name: id + in: path + description: 'ID of the run.' + required: true + schema: + type: integer + responses: + '200': + description: 'A run description' + content: + application/json: + schema: + $ref: '#/components/schemas/Run' + example: + run: + run_id: '100' + uploader: '1' + uploader_name: 'Jan van Rijn' + task_id: '28' + task_type: 'Supervised Classification' + task_evaluation_measure: predictive_accuracy + flow_id: '67' + flow_name: weka.BayesNet_K2(1) + setup_string: 'weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5' + parameter_setting: [{ name: D, value: 'true' }, { name: Q, value: weka.classifiers.bayes.net.search.local.K2 }, { name: P, value: '1' }, { name: S, value: BAYES }] + input_data: { dataset: { did: '28', name: optdigits, url: 'https:\\/\\/www.openml.org\\/data\\/download\\/28\\/dataset_28_optdigits.arff' } } + output_data: { file: [{ did: '48838', file_id: '261', name: description, url: 'https:\\/\\/www.openml.org\\/data\\/download\\/261\\/weka_generated_run935374685998857626.xml' }, { did: '48839', file_id: '262', name: predictions, url: 'https:\\/\\/www.openml.org\\/data\\/download\\/262\\/weka_generated_predictions576954524972002741.arff' }], evaluation: [{ name: area_under_roc_curve, flow_id: '4', value: '0.990288', array_data: '[0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]' }, { name: confusion_matrix, flow_id: '10', array_data: '[[544,1,0,0,7,0,1,0,0,1],[0,511,21,1,0,1,3,1,5,28],[0,7,511,1,0,1,0,3,23,11],[0,2,2,519,0,3,0,12,16,18],[0,3,0,0,528,0,4,21,6,6],[0,1,0,7,5,488,2,0,4,51],[1,7,0,0,2,0,548,0,0,0],[0,2,0,1,9,1,0,545,4,4],[1,25,2,2,3,6,2,1,503,9],[0,7,0,20,16,5,0,19,9,486]]' }, { name: f_measure, flow_id: '12', value: '0.922723', array_data: '[0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]' }, { name: kappa, flow_id: '13', value: '0.913601' }] } + '412': + description: 'Precondition failed. An error code and message are returned.\n220 - Please provide run ID. In order to view run details, please provide the run ID.\n221 - Run not found. The run ID was invalid, run does not exist (anymore).\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - run + summary: 'Delete run' + description: 'Deletes a run. Upon success, it returns the ID of the deleted run.' + operationId: 'Api_run::run_delete' + parameters: + - + name: id + in: path + description: 'Id of the run.' + required: true + schema: + type: integer + - + name: api_key + in: query + description: 'API key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'ID of the deleted run' + content: + application/json: + schema: + properties: + data_delete: { $ref: '#/components/schemas/inline_response_200_17_data_delete' } + type: object + example: + run_delete: + id: '2520' + '412': + description: 'Precondition failed. An error code and message are returned.\n390 - Please provide API key. In order to remove your content, please authenticate.\n391 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators\n392 - Run does not exists. The run ID could not be linked to an existing run.\n393 - Run is not owned by you. The run was owned by another user. Hence you cannot delete it.\n394 - Deleting run failed. Deleting the run failed. Please contact support team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/run/reset/{id}': + get: + tags: + - run + summary: 'Resets a run.' + description: 'Removes all run evaluations. When a run is reset, the runs will automatically be evaluated as soon as they are picked up by the evaluation engine again.' + operationId: 'Api_run::run_reset' + parameters: + - + name: id + in: path + description: 'Run ID.' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the evaluated run' + content: + application/json: + schema: + properties: + run_reset: { $ref: '#/components/schemas/inline_response_200_21_upload_flow' } + type: object + example: + run_reset: + id: '2520' + '412': + description: 'Precondition failed. An error code and message are returned.\n412 - Run does not exist\n413 - Run is not owned by you\n394 - Resetting run failed\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /run: + post: + tags: + - run + summary: 'Upload run' + description: 'Uploads a run. Upon success, it returns the run id.' + operationId: 'Api_run::run_upload' + parameters: + - + name: description + in: query + description: 'An XML file describing the dataset. Only name, description, and data format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/run).' + required: true + schema: + type: file + - + name: predictions + in: query + description: 'The predictions generated by the run' + required: true + schema: + type: file + - + name: model_readable + in: query + description: 'The human-readable model generated by the run' + required: false + schema: + type: file + - + name: model_serialized + in: query + description: 'The serialized model generated by the run' + required: false + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the uploaded run' + content: + application/json: + schema: + properties: + upload_flow: { $ref: '#/components/schemas/inline_response_200_18_upload_flow' } + type: object + example: + upload_run: + id: '2520' + '412': + description: 'Precondition failed. An error code and message are returned.\n201 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n202 - Please provide run XML.\n203 - Could not validate run xml by XSD. Please double check that the xml is valid.\n204 - Unknown task. The task with the given ID was not found in the database.\n205 - Unknown flow. The flow with the given ID was not found in the database.\n206 - Invalid number of files. The number of uploaded files did not match the number of files expected for the task type\n207 - File upload failed. One of the files uploaded has a problem.\n208 - Error inserting setup record. Please contact api administrators\n210 - Unable to store run. Please contact api administrators.\n211 - Dataset not in database. One of the datasets of the task was not included in database, please contact api administrators.\n212 - Unable to store file. Please contact api administrators.\n213 - Parameter in run xml unknown. One of the parameters provided in the run xml is not registered as parameter for the flow nor its components.\n214 - Unable to store input setting. Please contact API support team.\n215 - Unable to evaluate predictions. Please contact API support team.\n216 - Error thrown by Java Application. Additional information field is provided.\n217 - Error processing output data. Unknown or inconsistent evaluation measure. One of the provided evaluation measures could not be matched with a record in the math_function or flow table.\n218 - Wrong flow associated with run. The flow implements a math_function, which is unable to generate predictions. Please select another flow.\n219 - Error reading the XML document. The XML description file could not be verified.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/run/trace/{id}': + get: + tags: + - run + summary: 'Get run trace' + description: 'Returns the optimization trace of run. The trace contains every setup tried, its evaluation, and whether it was selected.' + operationId: 'Api_run::run_trace' + parameters: + - + name: id + in: path + description: 'ID of the run.' + required: true + schema: + type: integer + responses: + '200': + description: 'A run trace description' + content: + application/json: + schema: + $ref: '#/components/schemas/RunTrace' + example: + trace: + run_id: '573055' + trace_iteration: { repeat: '0', fold: '0', iteration: '1', setup_string: { parameter_minNumObj: '1', parameter_confidenceFactor: '0.25' }, evaluation: '94.074074', selected: 'true' } + '412': + description: 'Precondition failed. An error code and message are returned.\n570 - No successful trace associated with this run\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + tags: + - run + summary: 'Upload run trace' + description: 'Uploads a run trace. Upon success, it returns the run id.' + operationId: 'Api_run::run_trace_upload' + parameters: + - + name: id + in: path + description: 'ID of the run.' + required: true + schema: + type: integer + - + name: description + in: query + description: 'An XML file describing the trace. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.trace) and an [XML example](https://www.openml.org/api/v1/xml_example/run.trace).' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the run with the trace' + content: + application/json: + schema: + properties: + upload_flow: { $ref: '#/components/schemas/inline_response_200_23_upload_flow' } + type: object + example: + run_trace: + id: '2520' + '412': + description: 'Precondition failed. An error code and message are returned.\n561 - Problem with uploaded trace file.\n562 - Problem validating xml trace file.\n563 - Problem loading xml trace file.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /run/evaluate: + post: + tags: + - run + summary: 'Uploads a run evaluation' + description: 'Uploads a run evaluation. When successful, it returns the run id.' + operationId: 'Api_run::run_evaluate' + parameters: + - + name: description + in: query + description: 'An XML file describing the run evaluation.Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.run.evaluate) and an [XML example](https://www.openml.org/api/v1/xml_example/run.evaluate).' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the evaluated run' + content: + application/json: + schema: + properties: + upload_flow: { $ref: '#/components/schemas/inline_response_200_21_upload_flow' } + type: object + example: + run_evaluate: + id: '2520' + '412': + description: 'Precondition failed. An error code and message are returned.\n422 - Upload problem description XML\n423 - Problem validating uploaded description file\n424 - Problem opening description xml\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /setup/tag: + post: + tags: + - setup + summary: 'Tag a setup' + description: 'Tags a setup.' + operationId: 'Api_setup::setup_tag' + parameters: + - + name: setup_id + in: query + description: 'Id of the setup.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the tagged setup' + content: + application/json: + schema: + properties: + flow_tag: { $ref: '#/components/schemas/inline_response_200_15_flow_tag' } + type: object + example: + setup_tag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /setup/untag: + post: + tags: + - setup + summary: 'Untag a setup' + description: 'Untags a setup.' + operationId: 'Api_setup::setup_untag' + parameters: + - + name: setup_id + in: query + description: 'Id of the setup.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the untagged setup' + content: + application/json: + schema: + properties: + flow_untag: { $ref: '#/components/schemas/inline_response_200_16_flow_untag' } + type: object + example: + setup_untag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, flow, run} not found. The provided entity_id {data_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged\nby another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/setup/{id}': + get: + tags: + - setup + summary: 'Get a hyperparameter setup' + description: 'Returns information about a setup. The information includes the list of hyperparameters, with name, value, and default value.' + operationId: 'Api_setup::setup' + parameters: + - + name: id + in: path + description: 'ID of the hyperparameter setup (configuration). These IDs are stated in run descriptions.' + required: true + schema: + type: integer + responses: + '200': + description: 'A setup description' + content: + application/json: + schema: + $ref: '#/components/schemas/Setup' + example: + setup_parameters: + flow_id: '59' + parameter: [{ full_name: weka.JRip(1)_F, parameter_name: F, data_type: option, default_value: '3', value: '3' }, { full_name: weka.JRip(1)_N, parameter_name: 'N', data_type: option, default_value: '2.0', value: '2.0' }, { full_name: weka.JRip(1)_O, parameter_name: O, data_type: option, default_value: '2', value: '2' }, { full_name: weka.JRip(1)_S, parameter_name: S, data_type: option, default_value: '1', value: '1' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n280 - Please provide setup ID. In order to view setup details, please provide the run ID\n281 - Setup not found. The setup ID was invalid, or setup does not exist (anymore).\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - setup + summary: 'Delete setup' + description: 'Deletes a setup. Upon success, it returns the ID of the deleted setup.' + operationId: 'Api_setup::setup_delete' + parameters: + - + name: id + in: path + description: 'Id of the setup.' + required: true + schema: + type: integer + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'ID of the deleted setup' + content: + application/json: + schema: + properties: + study_delete: { $ref: '#/components/schemas/inline_response_200_14_study_delete' } + type: object + example: + setup_delete: + id: '1' + '412': + description: 'Precondition failed. An error code and message are returned.\n401 - Authentication failed. Please provide API key. In order to remove your content, please authenticate.\n402 - Setup does not exists. The setup ID could not be linked to an existing setup.\n404 - Setup deletion failed. Setup is in use by other content (runs, schedules, etc). Can not be deleted.\n405 - Setup deletion failed. Please try again later.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/setup/list/{filters}': + get: + tags: + - setup + summary: 'List and filter setups' + description: 'List setups, filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form ''/setup/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all evaluations that match the constraints. A maximum of 1,000 results are returned at a time, an error is returned if the result set is bigger. Use pagination (via limit and offset filters), or limit the results to certain flows, setups, or tags.' + operationId: 'Api_setup::setup_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /tag/{tag} - returns only setups tagged with the given tag.\r\n /flow/{ids} - return only setups for specific flows, specified as a comma-separated list of flow IDs, e.g. ''1,2,3''\r\n /setup/{ids} - return only specific setups, specified as a comma-separated list of setup IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of setup descriptions' + content: + application/json: + schema: + $ref: '#/components/schemas/SetupList' + example: + setups: + setup: [{ setup_id: '10', flow_id: '65', parameter: [{ id: '4144', flow_id: '65', flow_name: weka.RandomForest, full_name: weka.RandomForest(1)_I, parameter_name: I, data_type: option, default_value: '10', value: '10' }, { id: '4145', flow_id: '65', flow_name: weka.RandomForest, full_name: weka.RandomForest(1)_K, parameter_name: K, data_type: option, default_value: '0', value: '0' }] }] + '412': + description: 'Precondition failed. An error code and message are returned.\n670 - Please specify at least one filter.\n671 - Illegal filter.\n672 - Illegal filter input.\n673 - Result set too big. Please use one of the filters or the limit option.\n674 - No results, please check the filter.\n675 - Cannot specify offset without limit.\n676 - Requested result limit too high.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /study: + post: + tags: + - study + summary: 'Create new study' + description: 'Creates a new study. Upon success, it returns the study id.' + operationId: 'Api_study::study_create' + parameters: + - + name: description + in: query + description: 'An XML file describing the study. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.study.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/study).' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the uploaded study' + content: + application/json: + schema: + properties: + upload_study: { $ref: '#/components/schemas/inline_response_200_25_upload_study' } + type: object + example: + upload_study: + id: '4328' + '412': + description: 'Precondition failed. An error code and message are returned.\n1031 - Description file not present. Please upload the study description.\n1032 - Problem validating uploaded description file. The XML description format does not meet the standards. See the XSD schema.\n1033 - Illegal main entity type. Currently only collections of tasks and can be created.\n1034 - Linked entities are not of the correct type fot this study.\n1035 - Benchmark suites can only be linked to run studies.\n1036 - Referred benchmark suite cannot be found.\n1037 - Referred benchmark suite should be a task collection.\n1038 - Study alias is not unique.\n1039 - Dataset insertion problem. Please contact the administrators.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/study/{id}/attach': + post: + tags: + - study + summary: 'Attach a new entity to a study' + description: 'Attach a new entity to an exising study. Upon success, it returns the study id, type, and linked entities.' + operationId: 'Api_study::study_attach' + parameters: + - + name: id + in: path + description: 'Id of the study. Supplied in the URL path.' + required: true + schema: + type: integer + - + name: ids + in: query + description: 'Comma-separated list of entity IDs to be attached to the study. For instance, if this is a run study, the list of run IDs that need to be added (attached) to the study. Must be supplied as a POST variable.' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Properties of the updated study' + content: + application/json: + schema: + properties: + study_attach: { $ref: '#/components/schemas/inline_response_200_26_study_attach' } + type: object + example: + study_attach: + id: '1' + main_entity_type: task + linked_entities: '5' + '412': + description: 'Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannnot attach entities to legacy studies.\n1043 - Please provide POST field ''ids''.\n1044 - Please ensure that the ''ids'' in the POST field is a list of natural numbers.\n1045 - Could not attach entities to the study. It appears as if the entity does not exist.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/study/{id}/detach': + post: + tags: + - study + summary: 'Detach an entity from a study' + description: 'Detach an entity from an exising study. Upon success, it returns the study id, type, and linked entities.' + operationId: 'Api_study::study_detach' + parameters: + - + name: id + in: path + description: 'Id of the study.' + required: true + schema: + type: integer + - + name: ids + in: query + description: 'Comma-separated list of entity IDs to be detached from the study. For instance, if this is a run study, the list of run IDs that need to be removed (detached) from the study. Must be supplied as a POST variable.' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Properties of the updated study' + content: + application/json: + schema: + properties: + upload_study: { $ref: '#/components/schemas/inline_response_200_26_study_attach' } + type: object + example: + study_detach: + id: '1' + main_entity_type: task + linked_entities: '5' + '412': + description: 'Precondition failed. An error code and message are returned.\n1041 - Could not find study. Check the study ID in your request.\n1042 - Cannot attach entities to legacy studies.\n1043 - Please provide POST field ''ids''.\n1044 - Please ensure that the ''ids'' in the POST field is a list of natural numbers.\n1046 - Could not detach entities from the study. It appears as if the entity does not exist. \n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/study/{id}': + get: + tags: + - study + summary: 'Get study description by study id or alias' + description: 'Returns information about the study with the given id or alias.' + operationId: 'Api_study::study_get' + parameters: + - + name: id + in: path + description: 'ID or alias of the study.' + required: true + schema: + type: string + responses: + '200': + description: 'A study description' + content: + application/json: + schema: + $ref: '#/components/schemas/Study' + example: + study: + id: '99' + main_entity_type: task + name: 'CC18 benchmark suite' + description: 'CC18 benchmark suite' + creation_date: '2019-02-16T17:35:58' + creator: '1159' + data: { data_id: ['1', '2', '3'] } + tasks: { task_id: ['1', '2', '3'] } + '412': + description: 'Precondition failed. An error code and message are returned.\n601 - Unknown study. The study with the given id or alias was not found in the database\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - study + summary: 'Delete study' + description: 'Deletes a study. Upon success, it returns the ID of the deleted study.' + operationId: 'Api_study::study_delete' + parameters: + - + name: id + in: path + description: 'Id of the study.' + required: true + schema: + type: integer + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'ID of the deleted study' + content: + application/json: + schema: + properties: + study_delete: { $ref: '#/components/schemas/inline_response_200_24_study_delete' } + type: object + example: + study_delete: + id: '1' + '412': + description: 'Precondition failed. An error code and message are returned.\n591 - Please provide API key. In order to remove your content, please authenticate.\n592 - Study does not exists. The study ID could not be linked to an existing study.\n593 - Study deletion failed. Please try again later.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/study/list/{filters}': + get: + tags: + - study + summary: 'List all studies (collections of items)' + description: 'List studies, optionally filtered by a range of properties. Any number of properties can be combined by listing them one after the other in the form ''/study/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all studies that match the constraints.' + operationId: 'Api_study::study_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /main_entity_type/{type} - only return studies collecting entities of a given type (e.g. 'task' or 'run').\r\n /uploader/{ids} - return only evaluations uploaded by specific users, specified as a comma-separated list of user IDs, e.g. ''1,2,3''\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, results 11..15 will be returned. Both limit and offset need to be specified.\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of studies' + content: + application/json: + schema: + $ref: '#/components/schemas/StudyList' + example: + study_list: + study: [{ id: '1', alias: Study_1, name: 'A large-scale comparison of classification algorithms', creation_date: '2017-07-20 15:51:20', creator: '2' }, { id: '2', alias: Study_2, name: 'Fast Algorithm Selection using Learning Curves', creation_date: '2017-07-20 15:51:20', creator: '2' }] + '412': + description: 'Precondition failed. An error code and message are returned.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /task/tag: + post: + tags: + - task + summary: 'Tag a task' + description: 'Tags a task.' + operationId: 'Api_task::task_tag' + parameters: + - + name: task_id + in: query + description: 'Id of the task.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'The id of the tagged task' + content: + application/json: + schema: + properties: + task_tag: { $ref: '#/components/schemas/inline_response_200_6_task_tag' } + type: object + example: + task_tag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n470 - In order to add a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n471 - Entity not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n472 - Entity already tagged by this tag. The entity {dataset, task, flow, run} already had this tag.\n473 - Something went wrong inserting the tag. Please contact OpenML Team.\n474 - Internal error tagging the entity. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /task/untag: + post: + tags: + - task + summary: 'Untag a task' + description: 'Untags a task.' + operationId: 'Api_task::task_untag' + parameters: + - + name: task_id + in: query + description: 'Id of the task.' + required: true + schema: + type: integer + - + name: tag + in: query + description: 'Tag name' + required: true + schema: + type: string + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'A the features of the task' + content: + application/json: + schema: + properties: + task_untag: { $ref: '#/components/schemas/inline_response_200_7_task_untag' } + type: object + example: + task_untag: + id: '2' + '412': + description: 'Precondition failed. An error code and message are returned.\n475 - Please give entity_id {data_id, flow_id, run_id} and tag. In order to remove a tag, please upload the entity id (either data_id, task_id, flow_id, run_id) and tag (the name of the tag).\n476 - Entity {dataset, task, flow, run} not found. The provided entity_id {data_id, task_id, flow_id, run_id} does not correspond to an existing entity.\n477 - Tag not found. The provided tag is not associated with the entity {dataset, task, flow, run}.\n478 - Tag is not owned by you. The entity {dataset, flow, run} was tagged by another user. Hence you cannot delete it.\n479 - Internal error removing the tag. Please contact OpenML Team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/task/list/{filters}': + get: + tags: + - task + summary: 'List and filter tasks' + description: 'List tasks, possibly filtered by a range of properties from the task itself or from the underlying dataset. Any number of properties can be combined by listing them one after the other in the form ''/task/list/{filter}/{value}/{filter}/{value}/...'' Returns an array with all tasks that match the constraints.' + operationId: 'Api_task::task_list' + parameters: + - + name: filters + in: path + description: "Any combination of these filters\r\n /limit/{limit}/offset/{offset} - returns only {limit} results starting from result number {offset}. Useful for paginating results. With /limit/5/offset/10, tasks 11..15 will be returned. Both limit and offset need to be specified.\r\n /status/{status} - returns only tasks with a given status, either 'active', 'deactivated', or 'in_preparation'.\r\n /type/{type_id} - returns only tasks with a given task type id. See the list of task types of the ID's (e.g. 1 = Supervised Classification).\r\n /tag/{tag} - returns only tasks tagged with the given tag.\r\n /data_tag/{tag} - returns only tasks for which the underlying dataset is tagged with the given tag.\r\n /{data_quality}/{range} - returns only tasks for which the underlying datasets have certain qualities. {data_quality} can be data_id, data_name, number_instances, number_features, number_classes, number_missing_values. {range} can be a specific value or a range in the form 'low..high'. Multiple qualities can be combined, as in 'number_instances/0..50/number_features/0..10'.\r\n " + required: true + schema: + type: string + responses: + '200': + description: 'A list of tasks with the given tag' + content: + application/json: + schema: + $ref: '#/components/schemas/TaskList' + example: + task: + task: [{ task_id: '1', task_type: 'Supervised Classification', did: '1', name: anneal, status: active, format: ARFF, input: [{ name: estimation_procedure, value: '1' }, { name: evaluation_measures, value: predictive_accuracy }, { name: source_data, value: '1' }, { name: target_feature, value: class }], quality: [{ name: MajorityClassSize, value: '684' }, { name: MaxNominalAttDistinctValues, value: '10.0' }, { name: MinorityClassSize, value: '0' }, { name: NumBinaryAtts, value: '14.0' }, { name: NumberOfClasses, value: '6' }, { name: NumberOfFeatures, value: '39' }, { name: NumberOfInstances, value: '898' }, { name: NumberOfInstancesWithMissingValues, value: '0' }, { name: NumberOfMissingValues, value: '0' }, { name: NumberOfNumericFeatures, value: '6' }, { name: NumberOfSymbolicFeatures, value: '32' }], tag: [basic, study_1, study_7, under100k, under1m] }] + '412': + description: 'Precondition failed. An error code and message are returned.\n480 - Illegal filter specified.\n481 - Filter values/ranges not properly specified.\n482 - No results. There where no matches for the given constraints.\n483 - Can not specify an offset without a limit.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/task/{id}': + get: + tags: + - task + summary: 'Get task description' + description: 'Returns information about a task. The information includes the task type, input data, train/test sets, and more.' + operationId: 'Api_task::task' + parameters: + - + name: id + in: path + description: 'ID of the task.' + required: true + schema: + type: integer + responses: + '200': + description: 'A task description' + content: + application/json: + schema: + $ref: '#/components/schemas/Task' + example: + task: + task_id: '1' + task_type: 'Supervised Classification' + input: [{ name: source_data, data_set: { data_set_id: '1', target_feature: class } }, { name: estimation_procedure, estimation_procedure: { type: crossvalidation, data_splits_url: 'https://www.openml.org/api_splits/get/1/Task_1_splits.arff', parameter: [{ name: number_repeats, value: '1' }, { name: number_folds, value: '10' }, { name: percentage }, { name: stratified_sampling, value: 'true' }] } }, { name: cost_matrix, cost_matrix: [] }, { name: evaluation_measures, evaluation_measures: { evaluation_measure: predictive_accuracy } }] + output: { name: predictions, predictions: { format: ARFF, feature: [{ name: repeat, type: integer }, { name: fold, type: integer }, { name: row_id, type: integer }, { name: confidence.classname, type: numeric }, { name: prediction, type: string }] } } + tag: [basic, study_1, under100k, under1m] + '412': + description: 'Precondition failed. An error code and message are returned.\n150 - Please provide task_id.\n151 - Unknown task. The task with the given id was not found in the database\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - task + summary: 'Delete task' + description: 'Deletes a task. Upon success, it returns the ID of the deleted task.' + operationId: 'Api_task::task_delete' + parameters: + - + name: id + in: path + description: 'Id of the task.' + required: true + schema: + type: integer + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'ID of the deleted task' + content: + application/json: + schema: + properties: + task_delete: { $ref: '#/components/schemas/inline_response_200_4_task_delete' } + type: object + example: + task_delete: + id: '4328' + '412': + description: 'Precondition failed. An error code and message are returned.\n450 - Please provide API key. In order to remove your content, please authenticate.\n451 - Authentication failed. The API key was not valid. Please try to login again, or contact api administrators.\n452 - Task does not exists. The task ID could not be linked to an existing task.\n454 - Task is executed in some runs. Delete these first.\n455 - Deleting the task failed. Please contact support team.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /task: + post: + tags: + - task + summary: 'Upload task' + description: 'Uploads a task. Upon success, it returns the task id.' + operationId: 'Api_task::task_upload' + parameters: + - + name: description + in: query + description: 'An XML file describing the task. Only name, description, and task format are required. Also see the [XSD schema](https://www.openml.org/api/v1/xsd/openml.task.upload) and an [XML example](https://www.openml.org/api/v1/xml_example/task).' + required: true + schema: + type: file + - + name: api_key + in: query + description: 'Api key to authenticate the user' + required: true + schema: + type: string + responses: + '200': + description: 'Id of the uploaded task' + content: + application/json: + schema: + properties: + upload_task: { $ref: '#/components/schemas/inline_response_200_5_upload_task' } + type: object + example: + upload_task: + id: '4328' + '412': + description: 'Precondition failed. An error code and message are returned.\n530 - Description file not present. Please upload the task description.\n531 - Internal error. Please contact api support team\n532 - Problem validating uploaded description file. The XML description format does not meet the standards\n533 - Task already exists.\n534 - Error creating the task.\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /tasktype/list: + get: + tags: + - tasktype + summary: 'List all task types' + description: 'Returns an array with all task types in the system.' + responses: + default: + description: 'Unexpected error' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '200': + description: 'A task description' + content: + application/json: + schema: + $ref: '#/components/schemas/TaskTypeList' + example: + task_types: + task_type: [{ id: '1', name: 'Supervised Classification', description: 'In supervised classification, you are given ...', creator: 'Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl' }, { id: '2', name: 'Supervised Regression', description: 'Given a dataset with a numeric target ...', creator: 'Joaquin Vanschoren, Jan van Rijn, Luis Torgo, Bernd Bischl' }] + '/tasktype/{id}': + get: + tags: + - tasktype + summary: 'Get task type description' + description: 'Returns information about a task type. The information includes a description, the given inputs and the expected outputs.' + parameters: + - + name: id + in: path + description: 'ID of the task.' + required: true + schema: + type: integer + responses: + '200': + description: 'A task type description' + content: + application/json: + schema: + $ref: '#/components/schemas/TaskType' + example: + task_type: + id: '1' + name: 'Supervised Classification' + description: "In supervised classification, you are given an input dataset in which instances are labeled with a certain class. The goal is to build a model that predicts the class for future unlabeled instances. The model is evaluated using a train-test procedure, e.g. cross-validation.

\\\r\n * \\\r\n * To make results by different users comparable, you are given the exact train-test folds to be used, and you need to return at least the predictions generated by your model for each of the test instances. OpenML will use these predictions to calculate a range of evaluation measures on the server.

\\\r\n * \\\r\n * You can also upload your own evaluation measures, provided that the code for doing so is available from the implementation used. For extremely large datasets, it may be infeasible to upload all predictions. In those cases, you need to compute and provide the evaluations yourself.

\\\r\n * \\\r\n * Optionally, you can upload the model trained on all the input data. There is no restriction on the file format, but please use a well-known format or PMML." + creator: ['Joaquin Vanschoren', 'Jan van Rijn', 'Luis Torgo', 'Bernd Bischl'] + contributor: ['Bo Gao', 'Simon Fischer', 'Venkatesh Umaashankar', 'Michael Berthold', 'Bernd Wiswedel', 'Patrick Winter'] + creation_date: '2013-01-24 00:00:00' + input: [{ name: source_data, requirement: required, data_type: numeric }, { name: target_feature, requirement: required, data_type: string }, { name: estimation_procedure, requirement: required, data_type: numeric }, { name: cost_matrix, data_type: json }, { name: custom_testset, data_type: json }, { name: evaluation_measures, data_type: string }] + '412': + description: 'Precondition failed. An error code and message are returned.\n240 - Please provide task type ID.\n241 - Unknown task type. The task type with the given id was not found in the database\n' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /user/list: + get: + tags: + - user + summary: 'List all users by user id' + description: 'Returns an array with all user ids and names.' + operationId: 'Api_user::username_list' + responses: + '200': + description: 'A list of users' + content: + application/json: + schema: + $ref: '#/components/schemas/UserList' + example: + users: + user: [{ id: '1', username: janvanrijn@gmail.com }, { id: '2', username: joaquin.vanschoren@gmail.com }] +components: + schemas: + inline_response_200_16_flow_untag: + properties: + id: + description: 'ID of the untagged setup' + type: string + type: object + EvaluationMeasureList_evaluation_measures_measures: + properties: + measure: + description: 'The evaluation measure names' + type: array + items: + type: string + type: object + inline_response_200_10_flow_exists: + properties: + id: + description: 'The id of the flow with the given name and (external) version' + type: string + exists: + description: 'true or false' + type: string + type: object + Data_data_set_description: + properties: + default_target_attribute: + description: 'For tabular data, the name of the column that is typically used as the target attribute for that data set' + type: string + upload_date: + description: 'The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss' + type: string + version_label: + description: 'The version of the dataset, as defined by the uploader, for reference. Can be any format as long as it is unique.' + type: string + description: + description: 'Wiki description of the dataset, in (Git flavoured) markdown format' + type: string + format: + description: 'Data format, for instance ARFF' + type: string + url: + description: 'The URL where the data can be downloaded' + type: string + tag: + description: 'Tags added by OpenML users. Includes study tags in the form `study_1`' + type: array + items: + type: string + visibility: + description: 'Who can see the dataset. For instance `public`.' + type: string + md5_checksum: + description: 'Checksum to verify downloads of the dataset' + type: string + version: + description: 'The version of the dataset, set by OpenML. A positive integer' + type: string + status: + description: 'active, in_preparation, or deactivated' + type: string + file_id: + description: 'The ID of the dataset file stored on the OpenML server' + type: string + licence: + description: 'The licence granted for using the dataset, for instance Public or CC-BY' + type: string + original_data_url: + description: 'The URL where the original data is hosted.' + type: string + id: + description: 'ID of the dataset, a positive integer' + type: string + name: + description: 'The name of the dataset' + type: string + type: object + DataList_data_dataset: + properties: + did: + description: 'The dataset ID' + type: string + status: + description: 'The dataset status, either in_preparation, active, or deactivated' + type: string + quality: + type: array + items: + $ref: '#/components/schemas/DataList_data_quality' + name: + description: 'The dataset name' + type: string + format: + description: 'The data format of the dataset, e.g. ARFF' + type: string + type: object + Task_task_description_estimation_procedure: + properties: + parameter: + type: array + items: + $ref: '#/components/schemas/Task_task_description_estimation_procedure_parameter' + type: + description: 'The type of procedure, e.g. crossvalidation' + type: string + data_splits_url: + description: 'The url where the data splits can be downloaded' + type: string + type: object + DataList_data_quality: + properties: + name: + description: 'The name of the property' + type: string + value: + description: 'The value of the property' + type: string + type: object + FlowList_flows_flow: + properties: + full_name: + description: 'The full flow name (name + internal version number)' + type: string + external_version: + description: 'The external flow version' + type: string + version: + description: 'The internal flow version' + type: string + uploader: + description: 'The ID of the person who uploaded the flow' + type: string + id: + description: 'The flow ID' + type: string + name: + description: 'The flow name' + type: string + type: object + Run_run_description_input_data_dataset: + properties: + did: + description: 'The id of the dataset' + type: string + url: + description: 'The download url of the dataset' + type: string + name: + description: 'The name of the dataset' + type: string + type: object + EvaluationList_evaluations: + properties: + evaluation: + type: array + items: + $ref: '#/components/schemas/EvaluationList_evaluations_evaluation' + type: object + inline_response_200_14_study_delete: + properties: + id: + description: 'ID of the deleted setup, a positive integer' + type: string + type: object + DataUnprocessed: + properties: + data_unprocessed: + $ref: '#/components/schemas/DataUnprocessed_data_unprocessed' + type: object + DataUnprocessed_data_unprocessed_dataset: + properties: + did: + description: 'ID of the dataset a positive integer' + type: string + status: + description: 'Status of the dataset' + type: string + version: + description: 'Version of the dataset, a positive integer' + type: string + name: + description: 'The name of the dataset' + type: string + format: + description: 'The dataset format, e.g. ARFF' + type: string + type: object + EstimationProcedureList_estimationprocedures: + properties: + estimationprocedure: + type: array + items: + $ref: '#/components/schemas/EstimationProcedureList_estimationprocedures_estimationprocedure' + type: object + TaskTypeList: + properties: + task_types: + $ref: '#/components/schemas/TaskTypeList_task_types' + type: object + Run_run_description_output_data_evaluation: + properties: + array_data: + description: 'For composite evaluation measures (e.g. per-class measures, confusion matrix), a string (JSON) representation of the evaluation.' + type: string + name: + description: 'The name of the evaluation measure' + type: string + value: + description: 'The result of the evaluation' + type: string + flow_id: + description: 'The id of the code used to compute this evaluation method' + type: string + type: object + TaskList_task_quality: + properties: + name: + description: 'The name of the quality' + type: string + value: + description: 'The value of the quality' + type: string + type: object + Task_task_description: + properties: + input: + type: array + items: + $ref: '#/components/schemas/Task_task_description_input' + task_type: + description: 'The type of the task, e.g. Supervised Classification' + type: string + tag: + description: 'Tags added by OpenML uers. Includes study tags in the form ''study_1''' + type: array + items: + type: string + task_id: + description: 'ID of the task, a positive integer' + type: string + output: + type: array + items: + $ref: '#/components/schemas/Task_task_description_output' + type: object + inline_response_200_9_upload_flow: + properties: + id: + description: 'ID of the uploaded flow, a positive integer' + type: string + type: object + Flow_flow_description: + properties: + upload_date: + description: 'The datetime that the flow was uploaded, format yyyy-MM-dd HH:mm:ss' + type: string + description: + description: 'Wiki description of the flow, in (Git flavoured) markdown format' + type: string + language: + description: 'The programming language the flow is written in.' + type: string + parameter: + type: array + items: + $ref: '#/components/schemas/Flow_flow_description_parameter' + tag: + description: 'Tags added by OpenML users. Includes study tags in the form `study_1`' + type: array + items: + type: string + version: + description: 'The version of the flow, set by OpenML. A positive integer' + type: string + version_label: + description: 'The version of the flow, as defined by the uploader, for reference. Can be any format as long as it is unique.' + type: string + dependencies: + description: 'The libraries that this flow depends on, and their version numbers.' + type: string + uploader: + description: 'The uploader of the flow' + type: string + id: + description: 'ID of the flow, a positive integer' + type: string + name: + description: 'The name of the flow' + type: string + type: object + Task: + properties: + task_description: + $ref: '#/components/schemas/Task_task_description' + type: object + Setup: + properties: + setup_parameters: + $ref: '#/components/schemas/Setup_setup_parameters' + type: object + FlowList_flows: + properties: + flow: + type: array + items: + $ref: '#/components/schemas/FlowList_flows_flow' + type: object + RunTrace: + properties: + trace: + $ref: '#/components/schemas/RunTrace_trace' + type: object + Task_task_description_predictions_feature: + properties: + type: + description: 'The type of the prediction feature, e.g. integer' + type: string + name: + description: 'The name of the prediction feature, e.g. row_id' + type: string + type: object + inline_response_200_24_study_delete: + properties: + id: + description: 'ID of the deleted study, a positive integer' + type: string + type: object + inline_response_200_5_upload_task: + properties: + id: + description: 'ID of the uploaded task, a positive integer' + type: string + type: object + inline_response_200_13_flow_untag: + properties: + id: + description: 'ID of the untagged flow' + type: string + type: object + UserList_users: + properties: + user: + type: array + items: + $ref: '#/components/schemas/UserList_users_user' + type: object + DataFeatures: + properties: + data_features: + $ref: '#/components/schemas/DataFeatures_data_features' + type: object + Task_task_description_predictions: + properties: + feature: + type: array + items: + $ref: '#/components/schemas/Task_task_description_predictions_feature' + format: + description: 'The fromat of the predictions, e.g. ARFF' + type: string + type: object + inline_response_200_15_flow_tag: + properties: + id: + description: 'ID of the tagged setup' + type: string + type: object + Setup_setup_parameters: + properties: + parameter_setting: + type: array + items: + $ref: '#/components/schemas/Setup_setup_parameters_parameter_setting' + flow_id: + description: 'ID of the flow, a positive integer' + type: string + type: object + DataList: + properties: + data: + $ref: '#/components/schemas/DataList_data' + type: object + inline_response_200_26_study_attach: + properties: + linked_entities: + description: 'The number of linked entities' + type: string + main_entity_type: + description: 'Main entity type of the of the study' + type: string + id: + description: 'ID of the study, a positive integer' + type: string + type: object + inline_response_200_2_data_tag: + properties: + id: + description: 'ID of the tagged dataset' + type: string + type: object + inline_response_200_23_upload_flow: + properties: + id: + description: 'ID of the run with the trace, a positive integer' + type: string + type: object + EvaluationRequest_evaluation_request: + properties: + run: + $ref: '#/components/schemas/EvaluationRequest_evaluation_request_run' + type: object + Run: + properties: + run_description: + $ref: '#/components/schemas/Run_run_description' + type: object + inline_response_200_6_task_tag: + properties: + id: + description: 'ID of the tagged task' + type: string + type: object + RunList: + properties: + runs: + $ref: '#/components/schemas/RunList_runs' + type: object + inline_response_200_17_data_delete: + properties: + id: + description: 'ID of the deleted run, a positive integer' + type: string + type: object + Task_task_description_input: + properties: + data_set: + $ref: '#/components/schemas/Task_task_description_data_set' + cost_matrix: + description: 'The cost matrix, indicating the costs for each type of misclassification' + type: array + items: + type: array + items: + type: integer + format: int64 + name: + description: 'The name of the input, e.g. source_data' + type: string + evaluation_measures: + $ref: '#/components/schemas/Task_task_description_evaluation_measures' + estimation_procedure: + $ref: '#/components/schemas/Task_task_description_estimation_procedure' + type: object + UserList: + properties: + users: + $ref: '#/components/schemas/UserList_users' + type: object + Study_study_runs: + properties: + run_id: + type: array + items: + type: string + type: object + DataFeatures_data_features_feature: + properties: + index: + description: 'Feature index' + type: string + name: + description: 'Feature name' + type: string + data_type: + description: 'Feature data type' + type: string + is_target: + description: 'Whether this feature is seen as a target feature' + type: string + is_ignore: + description: 'Whether this feature should be ignored in modelling (e.g. every value is unique)' + type: string + is_row_identifier: + description: 'Whether this feature is a row identifier' + type: string + type: object + inline_response_200_21_upload_flow: + properties: + id: + description: 'ID of the evaluated run, a positive integer' + type: string + type: object + TaskType: + properties: + description: + description: 'A description of the task type' + type: string + date: + description: 'The date when the task type was created' + type: string + output: + type: array + items: + $ref: '#/components/schemas/TaskType_output' + contributor: + type: array + items: + type: string + input: + type: array + items: + $ref: '#/components/schemas/TaskType_input' + id: + description: 'ID of the task type, a positive integer' + type: string + name: + description: 'The name of the task type, e.g. Supervised Classification' + type: string + type: object + Run_run_description_output_data_file: + properties: + did: + description: 'The id of the uploaded file' + type: string + file_id: + description: 'The reference id of the uploaded file, for downloading afterward' + type: string + name: + description: 'The name of the uploaded file (e.g., description, predictions, model,...)' + type: string + type: object + TaskType_input: + properties: + data_set: + $ref: '#/components/schemas/Task_task_description_data_set' + cost_matrix: + type: array + items: + type: array + items: + type: integer + format: int64 + name: + description: 'The name of the input, e.g. source_data' + type: string + evaluation_measures: + $ref: '#/components/schemas/Task_task_description_evaluation_measures' + estimation_procedure: + $ref: '#/components/schemas/Task_task_description_estimation_procedure' + type: object + Run_run_description_output_data: + properties: + evaluation: + type: array + items: + $ref: '#/components/schemas/Run_run_description_output_data_evaluation' + file: + type: array + items: + $ref: '#/components/schemas/Run_run_description_output_data_file' + type: object + inline_response_200_3_data_untag: + properties: + id: + description: 'ID of the untagged dataset' + type: string + type: object + Task_task_description_output: + properties: + name: + description: 'The name of the output, e.g. predictions' + type: string + predictions: + $ref: '#/components/schemas/Task_task_description_predictions' + type: object + DataQualityList: + properties: + data_qualities_list: + $ref: '#/components/schemas/DataQualityList_data_qualities_list' + type: object + DataUnprocessed_data_unprocessed: + properties: + dataset: + $ref: '#/components/schemas/DataUnprocessed_data_unprocessed_dataset' + type: object + Study: + properties: + study: + $ref: '#/components/schemas/Study_study' + type: object + DataList_data: + properties: + dataset: + type: array + items: + $ref: '#/components/schemas/DataList_data_dataset' + type: object + UserList_users_user: + properties: + username: + description: 'The full user name' + type: string + id: + description: 'The user ID' + type: string + type: object + TaskType_output: + properties: + name: + description: 'The name of the output, e.g. predictions' + type: string + predictions: + $ref: '#/components/schemas/TaskType_predictions' + type: object + RunList_runs_run: + properties: + task_id: + description: 'The ID of the task solved by this run' + type: string + run_id: + description: 'The run ID' + type: string + error_message: + description: 'Error message generated by the run (if any)' + type: string + setup_id: + description: 'Ignore (internal representation of the parameter setting)' + type: string + uploader: + description: 'The ID of the person uploading this run' + type: string + flow_id: + description: 'The ID of the flow used in this run' + type: string + type: object + DataFeatures_data_features: + properties: + feature: + type: array + items: + $ref: '#/components/schemas/DataFeatures_data_features_feature' + type: object + DataQualities_data_qualities_quality: + properties: + name: + description: 'The name of the dataset quality measures' + type: string + value: + description: 'The value for this dataset' + type: string + type: object + inline_response_200_12_flow_tag: + properties: + id: + description: 'ID of the tagged flow' + type: string + type: object + inline_response_200_8_flow_delete: + properties: + id: + description: 'ID of the deleted flow, a positive integer' + type: string + type: object + DataQualities_data_qualities: + properties: + quality: + type: array + items: + $ref: '#/components/schemas/DataQualities_data_qualities_quality' + type: object + EvaluationRequest: + properties: + evaluation_request: + $ref: '#/components/schemas/EvaluationRequest_evaluation_request' + type: object + inline_response_200_18_upload_flow: + properties: + id: + description: 'ID of the uploaded run, a positive integer' + type: string + type: object + SetupList: + properties: + setups: + $ref: '#/components/schemas/SetupList_setups' + type: object + TaskList: + properties: + task: + $ref: '#/components/schemas/TaskList_task' + type: object + inline_response_200_4_task_delete: + properties: + id: + description: 'ID of the deleted task, a positive integer' + type: string + type: object + Task_task_description_data_set: + properties: + data_set_id: + description: 'The id of the dataset' + type: string + target_feature: + description: 'The name of the target feature for this task' + type: string + type: object + EvaluationList: + properties: + evaluations: + $ref: '#/components/schemas/EvaluationList_evaluations' + type: object + Run_run_description: + properties: + setup_string: + description: 'Configuration of the flow as a string, to be interpreted by the flow, its library, or command line interface.' + type: string + task_type: + description: 'The type of task solved by this run (e.g., classification)' + type: string + task_id: + description: 'The id of the task solved by this run' + type: string + task_evaluation_measure: + description: 'The evaluation measure that is supposed to be optimized in the task, if any' + type: string + uploader_name: + description: 'The name of the uploader of the run' + type: string + input_data: + $ref: '#/components/schemas/Run_run_description_input_data' + tag: + description: 'Tags added by OpenML users. Includes study tags in the form `study_1`' + type: array + items: + type: string + output_data: + $ref: '#/components/schemas/Run_run_description_output_data' + uploader: + description: 'The uploader of the run' + type: string + flow_id: + description: 'The id of the flow used in this run' + type: string + flow_name: + description: 'The name of the flow used in this run' + type: string + id: + description: 'ID of the run, a positive integer' + type: string + parameter_setting: + type: array + items: + $ref: '#/components/schemas/Run_run_description_parameter_setting' + type: object + FlowList: + properties: + flows: + $ref: '#/components/schemas/FlowList_flows' + type: object + inline_response_200_19_run_tag: + properties: + id: + description: 'ID of the tagged run' + type: string + type: object + Study_study_data: + properties: + data_id: + type: array + items: + type: string + type: object + inline_response_200_data_delete: + properties: + id: + description: 'ID of the deleted dataset, a positive integer' + type: string + type: object + RunTrace_trace: + properties: + trace_iteration: + type: array + items: + $ref: '#/components/schemas/RunTrace_trace_trace_iteration' + run_id: + description: 'run ID' + type: string + type: object + inline_response_200_7_task_untag: + properties: + id: + description: 'ID of the untagged task' + type: string + type: object + Data: + properties: + data_set_description: + $ref: '#/components/schemas/Data_data_set_description' + type: object + Task_task_description_evaluation_measures: + properties: + evaluation_measure: + description: 'The evaluation measure to optimize in this task' + type: string + type: object + SetupList_setups_parameter: + properties: + default_value: + description: 'The parameter''s default value' + type: string + data_type: + description: 'The parameter''s data type' + type: string + value: + description: 'The parameter value in this setup' + type: string + parameter_name: + description: 'The parameter''s short name' + type: string + full_name: + description: 'The parameter''s full name' + type: string + flow_id: + description: 'The (sub)flow ID' + type: string + flow_name: + description: 'The (sub)flow name' + type: string + id: + description: 'The parameter ID' + type: string + type: object + TaskList_task_input: + properties: + name: + description: 'The name of the input' + type: string + value: + description: 'The value of the input' + type: string + type: object + StudyList_study_list: + properties: + study: + type: array + items: + $ref: '#/components/schemas/StudyList_study_list_study' + type: object + Study_study_tag: + properties: + write_access: + description: 'The write access level of the study (e.g. public)' + type: string + name: + description: 'The name of the study (e.g. study_1)' + type: string + type: object + Flow: + properties: + flow_description: + $ref: '#/components/schemas/Flow_flow_description' + type: object + Run_run_description_parameter_setting: + properties: + name: + description: 'The name of the parameter' + type: string + value: + description: 'The value of the parameter used' + type: string + type: object + EvaluationRequest_evaluation_request_run: + properties: + setup_id: + description: 'ID of the setup, a positive integer' + type: string + upload_time: + description: 'The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss' + type: string + uploader: + description: 'ID of the uploader, a positive integer' + type: string + task_id: + description: 'ID of the task, a positive integer' + type: string + run_id: + description: 'ID of the run, a positive integer' + type: string + type: object + Error: + properties: + message: + type: string + code: + type: integer + additional_message: + type: string + type: object + EstimationProcedureList_estimationprocedures_estimationprocedure: + properties: + name: + description: 'The estimation procedure name, e.g. ''10 fold Crossvalidation''' + type: string + folds: + description: 'The number of cross-validation folds, e.g. ''10''' + type: string + stratified_sampling: + description: 'Whether or not the sampling is stratified, ''true'' or ''false''' + type: string + ttid: + description: 'The task type ID' + type: string + repeats: + description: 'The number of repeats, e.g. ''10''' + type: string + type: + description: 'The estimation procedure type, e.g. ''crossvalidation''' + type: string + id: + description: 'The estimation procedure ID' + type: string + type: object + TaskTypeList_task_types: + properties: + task_type: + type: array + items: + $ref: '#/components/schemas/TaskTypeList_task_types_task_type' + type: object + TaskList_task_task: + properties: + status: + description: 'The status of the source dataset, active, in_preparation, or deactivated' + type: string + task_type: + description: 'The type of task (e.g. Supervised Classificationr)' + type: string + name: + description: 'The name of the source dataset' + type: string + task_id: + description: 'The ID of the task' + type: string + format: + description: 'The format of the source dataset' + type: string + did: + description: 'The id of the source dataset' + type: string + tag: + type: array + items: + type: string + input: + type: array + items: + $ref: '#/components/schemas/TaskList_task_input' + quality: + type: array + items: + $ref: '#/components/schemas/TaskList_task_quality' + type: object + inline_response_200_20_run_untag: + properties: + id: + description: 'ID of the untagged run' + type: string + type: object + StudyList: + properties: + study_list: + $ref: '#/components/schemas/StudyList_study_list' + type: object + Study_study: + properties: + runs: + $ref: '#/components/schemas/Study_study_runs' + tasks: + $ref: '#/components/schemas/Study_study_tasks' + name: + description: 'The name of the study' + type: string + creator: + description: 'A comma-separated list of the study creators' + type: string + flows: + $ref: '#/components/schemas/Study_study_flows' + creation_date: + description: 'The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss' + type: string + alias: + description: 'The alias of the study' + type: string + tag: + $ref: '#/components/schemas/Study_study_tag' + main_entity_type: + description: 'The type of entity collected in the study (e.g. task or run)' + type: string + data: + $ref: '#/components/schemas/Study_study_data' + id: + description: 'The ID of the study' + type: string + type: object + inline_response_200_25_upload_study: + properties: + id: + description: 'ID of the uploaded study, a positive integer' + type: string + type: object + EvaluationList_evaluations_evaluation: + properties: + function: + description: 'The name of the evaluation function' + type: string + task_id: + description: 'The ID of the tasks solved by this run' + type: string + run_id: + description: 'The run ID' + type: string + array_data: + description: 'For structured evaluation measures, an array of evaluation values (e.g. per-class predictions, evaluation matrices,...)' + type: string + value: + description: 'The outcome of the evaluation' + type: string + flow_id: + description: 'The ID of the flow used by this run' + type: string + type: object + EstimationProcedureList: + properties: + estimationprocedures: + $ref: '#/components/schemas/EstimationProcedureList_estimationprocedures' + type: object + DataQualityList_data_qualities_list: + properties: + quality: + type: array + items: + type: string + type: object + Study_study_flows: + properties: + flow_id: + type: array + items: + type: string + type: object + StudyList_study_list_study: + properties: + alias: + description: 'The alias of the study' + type: string + creation_date: + description: 'The datetime that the dataset was uploaded, format yyyy-MM-dd HH:mm:ss' + type: string + creator: + description: 'A comma-separated list of the study creators' + type: string + id: + description: 'The ID of the study' + type: string + name: + description: 'The name of the study' + type: string + type: object + Run_run_description_input_data: + properties: + dataset: + $ref: '#/components/schemas/Run_run_description_input_data_dataset' + type: object + TaskTypeList_task_types_task_type: + properties: + description: + description: 'A description of the task type' + type: string + creator: + description: 'A comma-separated list of the task type creators' + type: string + id: + description: 'The ID of the task type' + type: string + name: + description: 'The name of the task type' + type: string + type: object + EvaluationMeasureList_evaluation_measures: + properties: + measures: + $ref: '#/components/schemas/EvaluationMeasureList_evaluation_measures_measures' + type: object + TaskType_predictions: + properties: + feature: + type: array + items: + $ref: '#/components/schemas/Task_task_description_predictions_feature' + format: + description: 'The format of the predictions, e.g. ARFF' + type: string + type: object + Flow_flow_description_parameter: + properties: + default_value: + description: 'The default value of the parameter' + type: string + name: + description: 'The name of the parameter' + type: string + data_type: + description: 'The data type of the parameter' + type: string + description: + description: 'A description of the parameter' + type: string + type: object + RunTrace_trace_trace_iteration: + properties: + setup_string: + description: 'A JSON representation of the setup (configuration)' + type: string + repeat: + description: 'The number of the repeat in the outer cross-valudation' + type: string + selected: + description: 'Whether this setup was selected as the best one (true or false)' + type: string + iteration: + description: 'A number of the optimization iteration' + type: string + fold: + description: 'The number of the fold in the inner cross-validation' + type: string + evaluation: + description: 'The evaluation score of the setup' + type: string + type: object + EvaluationMeasureList: + properties: + evaluation_measures: + $ref: '#/components/schemas/EvaluationMeasureList_evaluation_measures' + type: object + SetupList_setups: + properties: + setup: + type: array + items: + $ref: '#/components/schemas/SetupList_setups_setup' + type: object + TaskList_task: + properties: + task: + type: array + items: + $ref: '#/components/schemas/TaskList_task_task' + type: object + SetupList_setups_setup: + properties: + setup_id: + description: 'The setup ID' + type: string + parameter: + type: array + items: + $ref: '#/components/schemas/SetupList_setups_parameter' + flow_id: + description: 'The ID of the flow used by this run' + type: string + type: object + Task_task_description_estimation_procedure_parameter: + properties: + name: + description: 'The name of the parameter' + type: string + value: + description: 'The value of the parameter' + type: string + type: object + RunList_runs: + properties: + run: + type: array + items: + $ref: '#/components/schemas/RunList_runs_run' + type: object + Study_study_tasks: + properties: + task_id: + type: array + items: + type: string + type: object + inline_response_200_1_upload_data_set: + properties: + id: + description: 'ID of the uploaded dataset, a positive integer' + type: string + type: object + Setup_setup_parameters_parameter_setting: + properties: + default_value: + description: 'The default value of the parameter used' + type: string + value: + description: 'The value of the parameter used' + type: string + data_type: + description: 'The data type of the hyperparameter value' + type: string + full_name: + description: 'The full name of the hyperparameter' + type: string + parameter_name: + description: 'The short name of the hyperparameter' + type: string + type: object + DataQualities: + properties: + data_qualities: + $ref: '#/components/schemas/DataQualities_data_qualities' + type: object From b450f33ef41d3de73eb559188b1de674b00f6c33 Mon Sep 17 00:00:00 2001 From: mwever Date: Tue, 18 Aug 2020 17:21:38 +0200 Subject: [PATCH 14/15] tag_untag functions are now left out for tasks and runs. Instead within the classes two string constants are defined keeping the values for the super class providing the entity type and the entity special name. --- openml_OS/models/api/v1/Api_run.php | 11 +++++------ openml_OS/models/api/v1/Api_task.php | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/openml_OS/models/api/v1/Api_run.php b/openml_OS/models/api/v1/Api_run.php index d60e781c5..be5add8a5 100644 --- a/openml_OS/models/api/v1/Api_run.php +++ b/openml_OS/models/api/v1/Api_run.php @@ -3,6 +3,9 @@ class Api_run extends MY_Api_Model { + const ENTITY_TYPE = "run"; + const ENTITY_SPECIAL_NAME = "run"; + protected $version = 'v1'; function __construct() { @@ -162,7 +165,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { *) */ private function run_tag($run_id, $tag) { - $this->run_tag_untag($run_id, $tag, false); + $this->entity_tag_untag(self::ENTITY_TYPE, $run_id, $tag, false, self::ENTITY_SPECIAL_NAME); } /** @@ -224,11 +227,7 @@ private function run_tag($run_id, $tag) { *) */ private function run_untag($run_id, $tag) { - $this->run_tag_untag($run_id, $tag, true); - } - - private function run_tag_untag($run_id, $tag, $do_untag) { - $this->entity_tag_untag('run',$run_id, $tag, $do_untag, 'run'); + $this->entity_tag_untag(self::ENTITY_TYPE, $run_id, $tag, true, self::ENTITY_SPECIAL_NAME); } /** diff --git a/openml_OS/models/api/v1/Api_task.php b/openml_OS/models/api/v1/Api_task.php index cd95f2565..35ca249a0 100644 --- a/openml_OS/models/api/v1/Api_task.php +++ b/openml_OS/models/api/v1/Api_task.php @@ -3,6 +3,9 @@ class Api_task extends MY_Api_Model { protected $version = 'v1'; + const ENTITY_TYPE = "task"; + const ENTITY_SPECIAL_NAME = "task"; + function __construct() { parent::__construct(); @@ -130,7 +133,7 @@ function bootstrap($format, $segments, $request_type, $user_id) { *) */ private function task_tag($task_id, $tag) { - $this->task_tag_untag($task_id, $tag, false); + $this->entity_tag_untag(self::ENTITY_TYPE, $task_id, $tag, false, self::ENTITY_SPECIAL_NAME); } /** @@ -192,11 +195,7 @@ private function task_tag($task_id, $tag) { *) */ private function task_untag($task_id, $tag) { - $this->task_untag($task_id, $tag, true); - } - - private function task_tag_untag($task_id, $tag, $do_untag) { - $this->entity_tag_untag('task', $task_id, $tag, $do_untag, 'task'); + $this->entity_tag_untag(self::ENTITY_TYPE, $task_id, $tag, true, self::ENTITY_SPECIAL_NAME); } /** From e2fa4e9cb61774320072a7909f72a5e825d7ec94 Mon Sep 17 00:00:00 2001 From: mwever Date: Tue, 18 Aug 2020 18:15:54 +0200 Subject: [PATCH 15/15] Added some documentation for the OpenAPI specification generation and documentation with annotations within the PHP REST API. --- openapi/README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 openapi/README.md diff --git a/openapi/README.md b/openapi/README.md new file mode 100644 index 000000000..85f6b44c7 --- /dev/null +++ b/openapi/README.md @@ -0,0 +1,62 @@ +# OpenAPI REST API Documentation + +In order to facilitate interfacing with OpenML's REST API, it is desribed by means of OpenAPI. +However, in order to ensure that the specification is up-to-date with the current code base and the documentation does not need to be duplicated (once in the OpenAPI specification file and once in-line in the code base), in-line annotations are leveraged to facilitate the maintenance of the documentation. Via the composer package [zircote/swagger-php](https://zircote.github.io/swagger-php/), the annotations are compiled into an OpenAPI specification file (either yaml or json). Instructions on how to freshly generate the OpenAPI specification from the current code base as well as how to create/edit documentation for the REST API is given in the following. + +## Getting Started + +Prerequisite: In order to generate the up-to-date OpenAPI specification according to the current code base, you need to have the php-cli package installed on your device. + +For compiling in-line documentation into an OpenAPI specification file, the zircote/swagger-php package is used. This tool will allow you to search the entire code base and distill an OpenAPI specification from all found in-line comments. + +* Open a terminal and change into the `openapi/` directory. +* Install the zircote/swagger-php package by running the command `php composer.phar install` + +Now you have everything in place to start compiling an OpenAPI specification file from the code base. + +## Generate OpenML's Current OpenAPI Specification + +For generating OpenML's up-to-date OpenAPI specification, depending on your operating system, you may choose one of the scripts contained in the `openapi/` directory. +For all operating systems, there are two flavors of the script: one producing a yaml file and one a json file. The contents of both files will be the same except for the format itself. + +If you are a Linux of MacOS user, you may use the .sh-Scripts: + +``` +./generate_json_api.sh # This will compile the OpenAPI specification in JSON format. +./generate_yaml_api.sh # This will compile the OpenAPI specification in YAML format. +``` + +As a Windows user, you may want to use the following batch scripts: + +``` +./generate_json_api.bat # This will compile the OpenAPI specification in JSON format. +./generate_yaml_api.bat # This will compile the OpenAPI specification in YAML format. +``` + +## Instructions for the In-Line Documentation +The inline annotations for PHP work similar as in Java, following the schema @(). In case of the OpenAPI annotations the most important components such as the annotations for get, post, or put methods have distinct annotations. However, all OpenAPI specific annotations are namespaced with an `OA\`. Thus the annotation for get, post, and put correspond to `@OA\Get(...)`, `@OA\Post(...)`, and `@OA\Put(...)` respectively. The information about the path, tags, description, parameters, etc. is then enclosed within the parantheses. While keys such as path, tags, description, and so on which only take a simple value (instead of a complex object) are directly referenced, parameters for instance are specified with the help of distinct annotations again, i.e. `@OA\Parameter(...)`. + +A more comprehensive description of what kind of annotations are supported by the OpenAPI specification and more details on the PHP annotations please refer to the official [OpenAPI specification website](https://swagger.io/specification/) or the [zircote/swagger-php project page](https://zircote.github.io/swagger-php/). + +### Important Notice for Arrays + +Pay attention when specifying arrays within the PHP-Annotations. Brackets ([]) are not supported by the zircote/swagger-php compiler and need to be replaced by curly brackets ({}). It may seem to be a little awkward in the beginning, but eventually it works out. + +Example: Instead of specifying an object like +``` +{ + "a": [ 0, 1, 2 ] +} +``` +write +``` +{ + "a": { 0, 1, 2 } +} +``` +instead. + + +### Unused Schemas + +Schemas describe return types of the REST API. Since they can be nested into each other it is not always easy to see which of them are no longer needed. In order to identify orphan schemas, you can run the `unusedSchemas.py` Python script. The script will load the OpenAPI specification located in the current directory and check which schemas are indeed used and which are obsolete. Finally, if there are indeed orphan schemas, it will give out a list of schema names which are not referenced by any other schema or REST method. \ No newline at end of file