From 7ad37e5df2fed39639ecc3c1479c4f443bb8ca83 Mon Sep 17 00:00:00 2001 From: juliecoust Date: Wed, 13 Oct 2021 09:30:06 +0200 Subject: [PATCH] WIP improving API documentation #24 --- openapi.json | 42 ++++++++++++++++++++++++++-------------- py/API_models/imports.py | 18 ++++++++--------- py/main.py | 40 +++++++++++++++++++++++++++++--------- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/openapi.json b/openapi.json index 0fdde6e2..cec3e157 100644 --- a/openapi.json +++ b/openapi.json @@ -1879,7 +1879,7 @@ "projects" ], "summary":"Set Project Predict Settings", - "description":"**Update the project's prediction settings**, return **NULL upon success.**\n\nUnlike during full project update above, which needs high permissions, this entry point is accessible\nto project annotators, as it mirrors the prediction privileges.", + "description":"**Update the project's prediction settings**, return **NULL upon success.**\n\nšŸ”’ Unlike during full project update above, which needs high permissions, this entry point is accessible\nto **project annotators**, as it mirrors the prediction privileges.", "operationId":"set_project_predict_settings_projects__project_id__prediction_settings_put", "parameters":[ { @@ -1895,12 +1895,12 @@ "in":"path" }, { - "description":"The new prediction settings", + "description":"The new prediction settings.", "required":true, "schema":{ "title":"Settings", "type":"string", - "description":"The new prediction settings" + "description":"The new prediction settings." }, "example":"seltaxo=84963,59996,56545 baseproject=2562,2571", "name":"settings", @@ -2503,24 +2503,24 @@ "in":"path" }, { - "description":"\n\nSpecify the needed object (and ancilliary entities) fields.\n \nIt follows the naming convention 'prefix.field' : Prefix is either 'obj' for main object, 'fre' for free fields, 'img' for the visible image.\n\nThe column obj.imgcount contains the total count of images for the object.\n\nUse a comma to separate fields. \n ", + "description":"\n\nSpecify the needed object (and ancilliary entities) fields.\n \nIt follows the naming convention 'prefix.field' : Prefix is either 'obj' for main object, 'fre' for free fields, 'img' for the visible image.\n\nThe column obj.imgcount contains the total count of images for the object.\n\nUse a comma to separate fields. \n\nšŸ’” More help :\n\nYou can get the field labels by parsing the classiffieldlist returned by a call to https://ecotaxa.obs-vlfr.fr/api/docs#/projects/project_query_projects__project_id__get.\n\n**Note that the following fields must be prefixed with the header \"obj.\"** (for example ā†’ obj.orig_id):\n\nacquisid classif_auto_id, classif_auto_score, classif_auto_when, classif_crossvalidation_id, classif_qual, classif_qual,\nclassif_id, classif_qual, classif_who, classif_when, complement_info, depth_max, depth_min,\nlatitude, longitude, objdate, object_link, objid, objtime, orig_id, random_value, similarity, sunpos.\n\n**Note that the following fields must be prefixed with the header \"img.\"** (for example ā†’ img.file_name):\n\nfile_name, height, imgid, imgrank, file_name, orig, objid, file_name thumb_file_name, thumb_height, thumb_width, width.\n\n**Note that the following fields must be prefixed with the header \"txo.\"** (for example ā†’ txo.display_name):\n\ncreation_datetime, creator_email, display_name, id, id_instance, id_source, lastupdate_datetime,\nname, nbrobj, nbrobjcum, parent_id, rename_to source_desc, source_url, taxostatus, taxotype.\n\n**All other fields must be prefixed by the header \"fre.\"** (for example ā†’ fre.circ.).\n ", "required":false, "schema":{ "title":"Fields", "type":"string", - "description":"\n\nSpecify the needed object (and ancilliary entities) fields.\n \nIt follows the naming convention 'prefix.field' : Prefix is either 'obj' for main object, 'fre' for free fields, 'img' for the visible image.\n\nThe column obj.imgcount contains the total count of images for the object.\n\nUse a comma to separate fields. \n " + "description":"\n\nSpecify the needed object (and ancilliary entities) fields.\n \nIt follows the naming convention 'prefix.field' : Prefix is either 'obj' for main object, 'fre' for free fields, 'img' for the visible image.\n\nThe column obj.imgcount contains the total count of images for the object.\n\nUse a comma to separate fields. \n\nšŸ’” More help :\n\nYou can get the field labels by parsing the classiffieldlist returned by a call to https://ecotaxa.obs-vlfr.fr/api/docs#/projects/project_query_projects__project_id__get.\n\n**Note that the following fields must be prefixed with the header \"obj.\"** (for example ā†’ obj.orig_id):\n\nacquisid classif_auto_id, classif_auto_score, classif_auto_when, classif_crossvalidation_id, classif_qual, classif_qual,\nclassif_id, classif_qual, classif_who, classif_when, complement_info, depth_max, depth_min,\nlatitude, longitude, objdate, object_link, objid, objtime, orig_id, random_value, similarity, sunpos.\n\n**Note that the following fields must be prefixed with the header \"img.\"** (for example ā†’ img.file_name):\n\nfile_name, height, imgid, imgrank, file_name, orig, objid, file_name thumb_file_name, thumb_height, thumb_width, width.\n\n**Note that the following fields must be prefixed with the header \"txo.\"** (for example ā†’ txo.display_name):\n\ncreation_datetime, creator_email, display_name, id, id_instance, id_source, lastupdate_datetime,\nname, nbrobj, nbrobjcum, parent_id, rename_to source_desc, source_url, taxostatus, taxotype.\n\n**All other fields must be prefixed by the header \"fre.\"** (for example ā†’ fre.circ.).\n " }, "example":"obj.longitude,fre.feret", "name":"fields", "in":"query" }, { - "description":"order_field will order the result using given field. If prefixed with \"-\" then it will be reversed.", + "description":"Order the result using given field. If prefixed with \"-\" then it will be reversed.", "required":false, "schema":{ "title":"Order field", "type":"string", - "description":"order_field will order the result using given field. If prefixed with \"-\" then it will be reversed." + "description":"Order the result using given field. If prefixed with \"-\" then it will be reversed." }, "example":"obj.longitude", "name":"order_field", @@ -3582,7 +3582,7 @@ "Taxonomy Tree" ], "summary":"Reclassif Project Stats", - "description":"Dig into reclassification logs and **return the associations source ā†’ target for previous reclassifications.**", + "description":"Dig into reclassification logs and **return the associations (source ā†’ target) for previous reclassifications.**", "operationId":"reclassif_project_stats_taxa_reclassification_history__project_id__get", "parameters":[ { @@ -3603,7 +3603,13 @@ "description":"Successful Response", "content":{ "application/json":{ - "schema":{} + "schema":{ + "title":"Response Reclassif Project Stats Taxa Reclassification History Project Id Get", + "type":"array", + "items":{ + "type":"object" + } + } } } }, @@ -4221,7 +4227,7 @@ "content":{ "application/json":{ "schema":{ - "title":"#TODO JCE Reply Model", + "title":"Reply job question", "type":"object", "default":{} } @@ -5649,7 +5655,7 @@ "title":"Source path", "type":"string", "description":"Source path on server, to zip or plain directory. \n \n The path can be returned by a file upload (absolute), \n \n otherwise it's relative to shared file area root.", - "example":"" + "example":"/import_test.zip" }, "taxo_mappings":{ "title":"Taxo mappings", @@ -5666,12 +5672,14 @@ "skip_loaded_files":{ "title":"Skip loaded files", "type":"boolean", + "description":"If true skip loaded files, else don't.", "default":false, "example":false }, "skip_existing_objects":{ "title":"Skip existing objects", "type":"boolean", + "description":"If true skip existing objects, else don't.", "default":false, "example":false }, @@ -5706,7 +5714,9 @@ }, "description":"Errors from analysis.", "default":[], - "example":[] + "example":[ + "new TSV file(s) are not compliant" + ] } }, "description":"Import response. " @@ -7146,7 +7156,7 @@ "title":"Source path", "type":"string", "description":"Source path on server, to zip or plain directory.", - "example":"" + "example":"/import_test" }, "values":{ "title":"Constant values, per field, to write for all images. If a field has no value don't include it.", @@ -7202,7 +7212,11 @@ "type":"string" }, "description":"Validation errors, dry_run or not.", - "example":[] + "example":[ + "'abcde' is not a valid value for SimpleImportFields.latitude", + "'456.5' is not a valid value for SimpleImportFields.longitude", + "'very very low' is not a valid value for SimpleImportFields.depthmin" + ] } }, "description":"Simple Import, response. " diff --git a/py/API_models/imports.py b/py/API_models/imports.py index e2012b8e..46a784f8 100644 --- a/py/API_models/imports.py +++ b/py/API_models/imports.py @@ -7,21 +7,19 @@ from helpers.pydantic import BaseModel, Field -#TODO JCE - description class ImportReq(BaseModel): """ Import request, from UI choices. """ source_path: str = Field(title="Source path", description="Source path on server, to zip or plain directory." " \n \n The path can be returned by a file upload (absolute)," - " \n \n otherwise it's relative to shared file area root.", example="") + " \n \n otherwise it's relative to shared file area root.", example="/import_test.zip") taxo_mappings: Dict[str, str] = Field(title="Taxo mappings", description="Optional taxonomy mapping, the key specifies the taxonomy ID found in file and the value specifies the final taxonomy ID to write.", default={}, example={23444 : 76543}) - skip_loaded_files: bool = Field(default=False, title ="Skip loaded files", description="", example=False) - skip_existing_objects: bool = Field(default=False, title ="Skip existing objects", description="", example=False) + skip_loaded_files: bool = Field(default=False, title ="Skip loaded files", description="If true skip loaded files, else don't.", example=False) + skip_existing_objects: bool = Field(default=False, title ="Skip existing objects", description="If true skip existing objects, else don't.", example=False) update_mode: str = Field(title="Update mode", description="Update data ('Yes'), including classification ('Cla').", default="", example="Yes") class Config: schema_extra = {"title": "Import request Model"} -#TODO JCE - example class ImportRsp(BaseModel): """ Import response. """ job_id: int = Field(title="Job Id", description="The job which was created for the run.", example=1) @@ -38,7 +36,7 @@ class ImportRsp(BaseModel): # default={}) # warnings: List[str] = Field(title="Warnings from analysis", # default=[]) - errors: List[str] = Field(title="Errors", description="Errors from analysis.", default=[], example=[]) + errors: List[str] = Field(title="Errors", description="Errors from analysis.", default=[], example=["new TSV file(s) are not compliant"]) # rowcount: int = Field(title="Number of TSV rows, just counted during validation, or loaded", default=0) @@ -73,10 +71,9 @@ class SimpleImportFields(str, Enum): userlb = "userlb" status = "status" -#TODO JCE - example class SimpleImportReq(BaseModel): """ Simple Import request. """ - source_path: str = Field(title="Source path", description="Source path on server, to zip or plain directory.", example="") + source_path: str = Field(title="Source path", description="Source path on server, to zip or plain directory.", example="/import_test") values: Dict[SimpleImportFields, str] = Field( title="Constant values, per field, to write for all images. If a field has no value don't include it.", description=":" + ", ".join(SimpleImportFields), example={SimpleImportFields.latitude: 43.69, SimpleImportFields.longitude : 7.30}) @@ -88,8 +85,9 @@ class SimpleImportReq(BaseModel): class Config: schema_extra = {"title": "Simple import request Model"} -#TODO JCE - example class SimpleImportRsp(BaseModel): """ Simple Import, response. """ job_id: int = Field(title="Job Id", description="The job which was created for the run. 0 if called with dry_run option.", example=1) - errors: List[str] = Field(title="Errors", description="Validation errors, dry_run or not.", example=[]) + errors: List[str] = Field(title="Errors", description="Validation errors, dry_run or not.", example=["'abcde' is not a valid value for SimpleImportFields.latitude", + "'456.5' is not a valid value for SimpleImportFields.longitude", + "'very very low' is not a valid value for SimpleImportFields.depthmin"]) diff --git a/py/main.py b/py/main.py index 518043fc..18b9cd73 100644 --- a/py/main.py +++ b/py/main.py @@ -10,6 +10,7 @@ from fastapi import FastAPI, Request, Response, status, Depends, HTTPException, UploadFile, File, Query, Form, Body, \ Path +from fastapi import responses from fastapi.logger import logger as fastapi_logger from fastapi.responses import StreamingResponse, FileResponse from fastapi.templating import Jinja2Templates @@ -888,7 +889,7 @@ def update_project(project: ProjectModel, } } }) -def set_project_predict_settings(settings: str = Query(..., description="The new prediction settings", +def set_project_predict_settings(settings: str = Query(..., description="The new prediction settings.", example="seltaxo=84963,59996,56545 baseproject=2562,2571"), project_id: int = Path(..., description="Internal, numeric id of the project.", example=4223), @@ -896,8 +897,8 @@ def set_project_predict_settings(settings: str = Query(..., description="The new """ **Update the project's prediction settings**, return **NULL upon success.** - Unlike during full project update above, which needs high permissions, this entry point is accessible - to project annotators, as it mirrors the prediction privileges. + šŸ”’ Unlike during full project update above, which needs high permissions, this entry point is accessible + to **project annotators**, as it mirrors the prediction privileges. """ with ProjectsService() as sce: with RightsThrower(): @@ -1152,10 +1153,31 @@ def get_object_set(project_id: int = Path(..., description="Internal, numeric id The column obj.imgcount contains the total count of images for the object. -Use a comma to separate fields. +Use a comma to separate fields. + +šŸ’” More help : + +You can get the field labels by parsing the classiffieldlist returned by a call to https://ecotaxa.obs-vlfr.fr/api/docs#/projects/project_query_projects__project_id__get. + +**Note that the following fields must be prefixed with the header "obj."** (for example ā†’ obj.orig_id): + +acquisid classif_auto_id, classif_auto_score, classif_auto_when, classif_crossvalidation_id, classif_qual, classif_qual, +classif_id, classif_qual, classif_who, classif_when, complement_info, depth_max, depth_min, +latitude, longitude, objdate, object_link, objid, objtime, orig_id, random_value, similarity, sunpos. + +**Note that the following fields must be prefixed with the header "img."** (for example ā†’ img.file_name): + +file_name, height, imgid, imgrank, file_name, orig, objid, file_name thumb_file_name, thumb_height, thumb_width, width. + +**Note that the following fields must be prefixed with the header "txo."** (for example ā†’ txo.display_name): + +creation_datetime, creator_email, display_name, id, id_instance, id_source, lastupdate_datetime, +name, nbrobj, nbrobjcum, parent_id, rename_to source_desc, source_url, taxostatus, taxotype. + +**All other fields must be prefixed by the header "fre."** (for example ā†’ fre.circ.). ''', default=None, example="obj.longitude,fre.feret"), order_field: Optional[str] = Query(title="Order field", - description='order_field will order the result using given field. If prefixed with "-" then it will be reversed.', + description='Order the result using given field. If prefixed with "-" then it will be reversed.', default=None, example="obj.longitude"), # TODO: order_field should be a user-visible field name, not nXXX, in case of free field window_start: Optional[int] = Query(default=None, title="Window start", @@ -1591,14 +1613,14 @@ async def reclassif_stats(taxa_ids: str = Query(..., title="Taxa ids", ret = sce.most_used_non_advised(current_user, num_taxa_ids) return ret - -@app.get("/taxa/reclassification_history/{project_id}", tags=['Taxonomy Tree']) +#TODO JCE +@app.get("/taxa/reclassification_history/{project_id}", tags=['Taxonomy Tree'], response_model=List[Dict]) async def reclassif_project_stats( project_id: int = Path(..., description="Internal, numeric id of the project.", example=1), current_user: Optional[int] = Depends(get_optional_current_user)) \ -> List[TaxonBO]: """ - Dig into reclassification logs and **return the associations source ā†’ target for previous reclassifications.** + Dig into reclassification logs and **return the associations (source ā†’ target) for previous reclassifications.** """ with TaxonomyService() as sce: with RightsThrower(): @@ -1930,7 +1952,7 @@ def get_job(job_id: int = Path(..., description="Internal, the unique numeric id }) def reply_job_question( job_id: int = Path(..., description="Internal, the unique numeric id of this job.", example=47445), - reply: Dict[str, Any] = Body(default={}, title="#TODO JCE Reply Model"), + reply: Dict[str, Any] = Body(default={}, title="Reply job question"), current_user: int = Depends(get_current_user)) -> None: """ **Send answers to last question.** The job resumes after it receives the reply.