Skip to content

Commit

Permalink
add retry reprocessed endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
gferraro committed Dec 10, 2024
1 parent 53c9c75 commit 43c3bb8
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 37 deletions.
39 changes: 38 additions & 1 deletion api/api/V1/Reprocess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
import { idOf } from "../validation-middleware.js";
import { successResponse } from "./responseUtil.js";
import type { NextFunction } from "express-serve-static-core";
import { ClientError } from "../customErrors.js";
import { ClientError, BadRequestError } from "../customErrors.js";
import { arrayOf, jsonSchemaOf } from "../schema-validation.js";
import lodash from "lodash";
import RecordingIdSchema from "@schemas/api/common/RecordingId.schema.json" assert { type: "json" };
Expand All @@ -38,6 +38,43 @@ const { uniq: dedupe } = lodash;
export default (app: Application, baseUrl: string) => {
const apiUrl = `${baseUrl}/reprocess`;

/**
* @api {get} /api/v1/reprocess/retry/:id Retry processing a single recording which is in a failed state
* @apiName Reprocess
* @apiGroup Recordings
* @apiParam {Integer} id of recording to retry
* @apiDescription Retries processing a recording thats in a failed state
*
* @apiUse V1UserAuthorizationHeader
*
* @apiUse V1ResponseSuccess
* @apiUse V1ResponseError
*/
app.get(
`${apiUrl}/:id`,
extractJwtAuthorizedUser,
validateFields([idOf(param("id"))]),
fetchAuthorizedRequiredRecordingById(param("id")),
async (request: Request, response: Response, next) => {
if (!response.locals.recordings.isFailed()) {
return next(
new BadRequestError(
`Recording is not in a failed state '${response.locals.recordings.processingState}'`
)
);
}
if (await response.locals.recording.retryProcessing()) {
return successResponse(response, "Recording reprocessed");
} else {
return next(
new BadRequestError(
`Could not retry processing of recordings ${response.locals.recordings.id}`
)
);
}
}
);

/**
* @api {get} /api/v1/reprocess/:id Reprocess a single recording
* @apiName Reprocess
Expand Down
58 changes: 27 additions & 31 deletions api/classifications/classifications.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
import Classifications from "@/classifications/classification.json" assert { type: "json" };
const flattenNodes = (acc, node, parentPath) => {
for (const child of node.children || []) {
acc[child.label] = {
label: child.label,
display: child.display || child.label,
path: `${parentPath}.${child.label}`,
};
flattenNodes(acc, child, acc[child.label].path);
}
return acc;
for (const child of node.children || []) {
acc[child.label] = {
label: child.label,
display: child.display || child.label,
path: `${parentPath}.${child.label}`,
};
flattenNodes(acc, child, acc[child.label].path);
}
return acc;
};
export const flatClassifications = (() => {
const nodes = flattenNodes({}, Classifications, "all");
if (nodes.unknown) {
nodes["unidentified"] = nodes["unknown"];
}
return nodes;
const nodes = flattenNodes({}, Classifications, "all");
if (nodes.unknown) {
nodes["unidentified"] = nodes["unknown"];
}
return nodes;
})();
export const displayLabelForClassificationLabel = (
label,
aiTag = false,
isAudioContext = false
) => {
label = label.toLowerCase();
if (label === "unclassified") {
return "AI Queued";
}
if (label === "unidentified" && aiTag) {
return "Unidentified";
}
const classifications = flatClassifications;
if ((label === "human" || label === "person") && !isAudioContext) {
return "human";
}
return (classifications[label] && classifications[label].display) || label;
export const displayLabelForClassificationLabel = (label, aiTag = false, isAudioContext = false) => {
label = label.toLowerCase();
if (label === "unclassified") {
return "AI Queued";
}
if (label === "unidentified" && aiTag) {
return "Unidentified";
}
const classifications = flatClassifications;
if ((label === "human" || label === "person") && !isAudioContext) {
return "human";
}
return (classifications[label] && classifications[label].display) || label;
};
12 changes: 11 additions & 1 deletion api/models/Recording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export interface Recording extends Sequelize.Model, ModelCommon<Recording> {
getGroup: () => Promise<Group>;

getActiveTracksTagsAndTagger: () => Promise<any>;

retryProcessing: () => Promise<Recording>;
reprocess: () => Promise<Recording>;
filterData: (options: any) => void;
// NOTE: Implicitly created by sequelize associations (along with other
Expand Down Expand Up @@ -787,6 +787,16 @@ from (
};
}

// retry processing this recording
Recording.prototype.retryProcessing = async function () {
if (!this.processingState.endsWith(".failed")) {
return null;
}
await this.update({
processingState: this.processingState.replace(".failed", ""),
});
};

// reprocess a recording and set all active tracks to archived
Recording.prototype.reprocess = async function () {
const tags = await this.getTags();
Expand Down
6 changes: 3 additions & 3 deletions browse/src/api/Recording.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,8 @@ function deleteRecordingTag(
return CacophonyApi.delete(`${apiPath}/${id}/tags/${tagId}`);
}

function reprocess(id: RecordingId): Promise<FetchResult<void>> {
return CacophonyApi.get(`/api/v1/reprocess/${id}`);
function retryProcessing(id: RecordingId): Promise<FetchResult<void>> {
return CacophonyApi.get(`/api/v1/reprocess/retry/${id}`);
}

function thumbnail(id: RecordingId): string {
Expand Down Expand Up @@ -641,7 +641,7 @@ export default {
deleteTrack,
undeleteTrack,
updateTrack,
reprocess,
retryProcessing,
addTrackTag,
deleteTrackTag,
replaceTrackTag,
Expand Down
2 changes: 1 addition & 1 deletion browse/src/components/Video/VideoRecording.vue
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export default {
},
methods: {
async reprocess() {
const { success } = await api.recording.reprocess(this.recordingId);
const { success } = await api.recording.retryProcessing(this.recordingId);
if (success) {
this.$emit("recording-updated", {
id: this.recordingId,
Expand Down

0 comments on commit 43c3bb8

Please sign in to comment.