Implement rename class api

This commit is contained in:
Nicolas Mowen 2025-11-08 12:25:54 -07:00
parent c3993eec22
commit 31b8357fc2
3 changed files with 127 additions and 3 deletions

View File

@ -680,6 +680,97 @@ def delete_classification_dataset_images(
) )
@router.put(
"/classification/{name}/dataset/{old_category}/rename",
response_model=GenericResponse,
dependencies=[Depends(require_role(["admin"]))],
summary="Rename a classification category",
description="""Renames a classification category for a given classification model.
The old category must exist and the new name must be valid. Returns a success message or an error if the name is invalid.""",
)
def rename_classification_category(
request: Request, name: str, old_category: str, body: dict = None
):
config: FrigateConfig = request.app.frigate_config
if name not in config.classification.custom:
return JSONResponse(
content=(
{
"success": False,
"message": f"{name} is not a known classification model.",
}
),
status_code=404,
)
json: dict[str, Any] = body or {}
new_category = sanitize_filename(json.get("new_category", ""))
if not new_category:
return JSONResponse(
content=(
{
"success": False,
"message": "New category name is required.",
}
),
status_code=400,
)
old_folder = os.path.join(
CLIPS_DIR, sanitize_filename(name), "dataset", sanitize_filename(old_category)
)
new_folder = os.path.join(
CLIPS_DIR, sanitize_filename(name), "dataset", new_category
)
if not os.path.exists(old_folder):
return JSONResponse(
content=(
{
"success": False,
"message": f"Category {old_category} does not exist.",
}
),
status_code=404,
)
if os.path.exists(new_folder):
return JSONResponse(
content=(
{
"success": False,
"message": f"Category {new_category} already exists.",
}
),
status_code=400,
)
try:
os.rename(old_folder, new_folder)
return JSONResponse(
content=(
{
"success": True,
"message": f"Successfully renamed category to {new_category}.",
}
),
status_code=200,
)
except Exception as e:
logger.error(f"Error renaming category: {e}")
return JSONResponse(
content=(
{
"success": False,
"message": f"Failed to rename category: {str(e)}",
}
),
status_code=500,
)
@router.post( @router.post(
"/classification/{name}/dataset/categorize", "/classification/{name}/dataset/categorize",
response_model=GenericResponse, response_model=GenericResponse,

View File

@ -22,7 +22,8 @@
"categorizedImage": "Successfully Classified Image", "categorizedImage": "Successfully Classified Image",
"trainedModel": "Successfully trained model.", "trainedModel": "Successfully trained model.",
"trainingModel": "Successfully started model training.", "trainingModel": "Successfully started model training.",
"updatedModel": "Successfully updated model configuration" "updatedModel": "Successfully updated model configuration",
"renamedCategory": "Successfully renamed class to {{name}}"
}, },
"error": { "error": {
"deleteImageFailed": "Failed to delete: {{errorMessage}}", "deleteImageFailed": "Failed to delete: {{errorMessage}}",
@ -30,7 +31,8 @@
"deleteModelFailed": "Failed to delete model: {{errorMessage}}", "deleteModelFailed": "Failed to delete model: {{errorMessage}}",
"categorizeFailed": "Failed to categorize image: {{errorMessage}}", "categorizeFailed": "Failed to categorize image: {{errorMessage}}",
"trainingFailed": "Failed to start model training: {{errorMessage}}", "trainingFailed": "Failed to start model training: {{errorMessage}}",
"updateModelFailed": "Failed to update model: {{errorMessage}}" "updateModelFailed": "Failed to update model: {{errorMessage}}",
"renameCategoryFailed": "Failed to rename class: {{errorMessage}}"
} }
}, },
"deleteCategory": { "deleteCategory": {

View File

@ -187,6 +187,37 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
null, null,
); );
const onRename = useCallback(
(old_name: string, new_name: string) => {
axios
.put(`/classification/${model.name}/dataset/${old_name}/rename`, {
new_category: new_name,
})
.then((resp) => {
if (resp.status == 200) {
toast.success(
t("toast.success.renamedCategory", { name: new_name }),
{
position: "top-center",
},
);
setPageToggle(new_name);
refreshDataset();
}
})
.catch((error) => {
const errorMessage =
error.response?.data?.message ||
error.response?.data?.detail ||
"Unknown error";
toast.error(t("toast.error.renameCategoryFailed", { errorMessage }), {
position: "top-center",
});
});
},
[model, setPageToggle, refreshDataset, t],
);
const onDelete = useCallback( const onDelete = useCallback(
(ids: string[], isName: boolean = false, category?: string) => { (ids: string[], isName: boolean = false, category?: string) => {
const targetCategory = category || pageToggle; const targetCategory = category || pageToggle;
@ -354,7 +385,7 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
trainImages={trainImages || []} trainImages={trainImages || []}
setPageToggle={setPageToggle} setPageToggle={setPageToggle}
onDelete={onDelete} onDelete={onDelete}
onRename={() => {}} onRename={onRename}
/> />
</div> </div>
)} )}