Skip to content

Commit

Permalink
Merge pull request #288 from tnc-ca-geo/delete-camera-fix
Browse files Browse the repository at this point in the history
Delete camera polish
  • Loading branch information
nathanielrindlaub authored Jan 4, 2025
2 parents db4309e + 0ef35b3 commit b4d027d
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 54 deletions.
2 changes: 2 additions & 0 deletions src/api/db/models/Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ export class CameraModel {
});
}
}

// remove registration for curr_project
const currProjIndex = cam.projRegistrations.findIndex((pr) => pr.projectId === projectId);
cam.projRegistrations.splice(currProjIndex, 1);
await cam.save();
Expand Down
6 changes: 0 additions & 6 deletions src/api/db/models/Image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ export class ImageModel {
}
const s3 = new S3.S3Client({ region: process.env.AWS_DEFAULT_REGION });

console.time('delete-images total');
console.time('delete-images mongo records');
const images = await Image.find({ _id: { $in: input.imageIds! } });

if (images.length !== 0) {
Expand Down Expand Up @@ -196,12 +194,10 @@ export class ImageModel {
} finally {
// Ending the session
await session.endSession();
console.timeEnd('delete-images mongo records');
}
}

const keys: { Key: string }[] = [];
console.time('delete-images s3 records');
input.imageIds!.forEach((id) => {
const image = images.find((i) => idMatch(i._id, id));
['medium', 'original', 'small'].forEach((size) => {
Expand All @@ -214,8 +210,6 @@ export class ImageModel {
Delete: { Objects: keys },
}),
);
console.timeEnd('delete-images s3 records');
console.timeEnd('delete-images total');

return {
isOk: s3Res.Errors === undefined || s3Res.Errors.length === 0,
Expand Down
11 changes: 11 additions & 0 deletions src/api/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,14 @@ export class CameraRegistrationError extends GraphQLError {
});
}
}

export class DeleteCameraError extends GraphQLError {
constructor(message = 'DeleteCameraError', properties = {}) {
super(message, {
extensions: {
code: 'DELETE_CAMERA_ERROR',
...properties,
},
});
}
}
8 changes: 4 additions & 4 deletions src/task/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export class AnnotationsExport {
return count;
}

async toCSV(): Promise<AnnotationResponse> {
async toCSV(): Promise<AnnotationOutput> {
console.log('exporting to CSV');

try {
Expand Down Expand Up @@ -173,7 +173,7 @@ export class AnnotationsExport {
};
}

async toCOCO(): Promise<AnnotationResponse> {
async toCOCO(): Promise<AnnotationOutput> {
console.log('exporting to coco');
try {
// create categories map & string
Expand Down Expand Up @@ -570,7 +570,7 @@ export class AnnotationsExport {
export default async function (
task: TaskInput<{ filters: FiltersSchema; format: any }> & { _id: string },
config: Config,
) {
): Promise<AnnotationOutput> {
const dataExport = new AnnotationsExport(
{
projectId: task.projectId,
Expand All @@ -592,7 +592,7 @@ export default async function (
}
}

interface AnnotationResponse {
interface AnnotationOutput {
url: string;
count: number;
meta: { reviewedCount: { reviewed: number; notReviewed: number } };
Expand Down
80 changes: 40 additions & 40 deletions src/task/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,54 @@ import { type TaskInput } from '../api/db/models/Task.js';
import type * as gql from '../@types/graphql.js';
import { ProjectModel } from '../api/db/models/Project.js';
import { DeleteImagesByFilter } from './image.js';
import { DeleteCameraError } from '../api/errors.js';

export async function UpdateSerialNumber(task: TaskInput<gql.UpdateCameraSerialNumberInput>) {
export async function UpdateSerialNumber(
task: TaskInput<gql.UpdateCameraSerialNumberInput>,
): Promise<gql.StandardPayload> {
const context = { user: { is_superuser: true, curr_project: task.projectId } as User };
return await CameraModel.updateSerialNumber(task.config, context);
}

export async function DeleteCamera(task: TaskInput<gql.DeleteCameraInput>) {
export async function DeleteCamera(
task: TaskInput<gql.DeleteCameraInput>,
): Promise<{ isOk: boolean; errors: any[] }> {
const context = { user: { is_superuser: true, curr_project: task.projectId } as User };
console.log('CameraModel.deleteCameraConfig - input: ', task.config);
const { cameraId } = task.config;
const errors = [];
try {
// Step 1: delete deployments from views
await ProjectModel.removeCameraFromViews(
{
cameraId: task.config.cameraId,
},
context,
);
// Step 2: delete camera record from project
await ProjectModel.deleteCameraConfig(
{
cameraId: task.config.cameraId,
},
context,
);

// Step3: delete images associated with this camera
const deleteRes = await DeleteImagesByFilter({
projectId: task.projectId,
config: {
filters: {
cameras: [task.config.cameraId],
},
},
type: 'DeleteImagesByFilter',
user: task.user,
});
if (deleteRes.errors) {
errors.push(...deleteRes.errors);
}
// Step 4: unregister camera
if (
(await CameraModel.getWirelessCameras({ _ids: [task.config.cameraId] }, context)).length > 0
) {
await CameraModel.removeProjectRegistration({ cameraId: task.config.cameraId }, context);
}
} catch (err) {
return { isOk: false, error: err };

// Prevent deleting wireless cameras from default_project
const wirelessCam = await CameraModel.getWirelessCameras({ _ids: [cameraId] }, context);
const isWirelessCam = wirelessCam.length > 0;
if (isWirelessCam && task.projectId === 'default_project') {
throw new DeleteCameraError('You cannot delete wireless cameras from the Default Project');
}

// Step 1: delete deployments from views
await ProjectModel.removeCameraFromViews({ cameraId }, context);

// Step 2: delete camera record from project
await ProjectModel.deleteCameraConfig({ cameraId }, context);

// Step 3: delete images associated with this camera
const deleteRes = await DeleteImagesByFilter({
projectId: task.projectId,
config: { filters: { cameras: [cameraId] } },
type: 'DeleteImagesByFilter',
user: task.user,
});

if (deleteRes.errors) {
errors.push(...deleteRes.errors);
}

// Step 4: if wireless camera, unregister camera
if (isWirelessCam) {
await CameraModel.removeProjectRegistration({ cameraId }, context);
}

// Note: `errors` is for any errors that occurred during the delete images step
// the task will be marked as complete and the errors are not displayed to the user
return { isOk: true, errors: errors };
}
10 changes: 7 additions & 3 deletions src/task/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { ImageModel } from '../api/db/models/Image.js';
import { TaskInput } from '../api/db/models/Task.js';
import type * as gql from '../@types/graphql.js';

export async function DeleteImagesByFilter(task: TaskInput<gql.DeleteImagesByFilterTaskInput>) {
export async function DeleteImagesByFilter(
task: TaskInput<gql.DeleteImagesByFilterTaskInput>,
): Promise<{ filters: gql.FiltersInput; errors: any[] }> {
/**
* Deletes images that match the inputted filters in batches of 300.
* This is used by the frontend to delete all images currently shown.
Expand Down Expand Up @@ -40,7 +42,9 @@ export async function DeleteImagesByFilter(task: TaskInput<gql.DeleteImagesByFil
return { filters: task.config.filters, errors: errors };
}

export async function DeleteImages(task: TaskInput<gql.DeleteImagesInput>) {
export async function DeleteImages(
task: TaskInput<gql.DeleteImagesInput>,
): Promise<{ imageIds: String[]; errors: any[] }> {
/**
* Deletes a list of images by their IDs in batches of 300.
* This is used by the frontend when the user is selecting more than 300 images to delete to delete at once.
Expand All @@ -57,5 +61,5 @@ export async function DeleteImages(task: TaskInput<gql.DeleteImagesInput>) {
errors.push(...res.errors);
}
}
return { imageIds: task.config.imageIds, errors: errors };
return { imageIds: task.config.imageIds as String[], errors: errors };
}
12 changes: 11 additions & 1 deletion src/task/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import _ from 'lodash';
import { type TaskInput } from '../api/db/models/Task.js';
import { type FiltersSchema } from '../api/db/schemas/Project.js';

export default async function (task: TaskInput<{ filters: FiltersSchema }>) {
export default async function (
task: TaskInput<{ filters: FiltersSchema }>,
): Promise<GetStatsOutput> {
const context = { user: { is_superuser: true, curr_project: task.projectId } };
let imageCount = 0;
let reviewed = 0;
Expand Down Expand Up @@ -71,6 +73,14 @@ export default async function (task: TaskInput<{ filters: FiltersSchema }>) {
};
}

interface GetStatsOutput {
imageCount: number;
reviewedCount: { reviewed: number; notReviewed: number };
reviewerList: Reviewer[];
labelList: Record<string, number>;
multiReviewerCount: number;
}

interface Reviewer {
userId: string;
reviewedCount: number;
Expand Down

0 comments on commit b4d027d

Please sign in to comment.