Skip to content

Commit

Permalink
504 on save annotation, workaround (#7286)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekachev authored Dec 27, 2023
1 parent 06c6a76 commit fb610a4
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 193 deletions.
4 changes: 4 additions & 0 deletions changelog.d/20231225_115001_boris_retry_if_504.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Fixed

- Creating duplicating annotations when nginx throws 504 timeout status (workaround)
(<https://github.com/opencv/cvat/pull/7286>)
2 changes: 1 addition & 1 deletion cvat-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "14.0.0",
"version": "14.0.1",
"type": "module",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "src/api.ts",
Expand Down
4 changes: 2 additions & 2 deletions cvat-core/src/annotations-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ObjectState from './object-state';
import { getAnnotations, getCollection } from './annotations';

export interface SingleFrameActionInput {
collection: Omit<SerializedCollection, 'tracks' | 'tags'>;
collection: Omit<SerializedCollection, 'tracks' | 'tags' | 'version'>;
frameData: {
width: number;
height: number;
Expand All @@ -20,7 +20,7 @@ export interface SingleFrameActionInput {
}

export interface SingleFrameActionOutput {
collection: Omit<SerializedCollection, 'tracks' | 'tags'>;
collection: Omit<SerializedCollection, 'tracks' | 'tags' | 'version'>;
}

export enum ActionParameterType {
Expand Down
12 changes: 6 additions & 6 deletions cvat-core/src/annotations-collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
SkeletonShape, SkeletonTrack, PolygonShape, CuboidShape,
RectangleShape, PolylineShape, PointsShape, EllipseShape,
} from './annotations-objects';
import { SerializedCollection, SerializedTrack } from './server-response-types';
import { SerializedCollection, SerializedShape, SerializedTrack } from './server-response-types';
import AnnotationsFilter from './annotations-filter';
import { checkObjectType } from './common';
import Statistics from './statistics';
Expand Down Expand Up @@ -116,7 +116,7 @@ export default class Collection {
};
}

import(data: SerializedCollection): ImportedCollection {
import(data: Omit<SerializedCollection, 'version'>): ImportedCollection {
const result = {
tags: [],
shapes: [],
Expand Down Expand Up @@ -159,16 +159,16 @@ export default class Collection {
return result;
}

export(): SerializedCollection {
export(): Omit<SerializedCollection, 'version'> {
const data = {
tracks: this.tracks.filter((track) => !track.removed).map((track) => track.toJSON()),
tracks: this.tracks.filter((track) => !track.removed).map((track) => track.toJSON() as SerializedTrack),
shapes: Object.values(this.shapes)
.reduce((accumulator, frameShapes) => {
accumulator.push(...frameShapes);
return accumulator;
}, [])
.filter((shape) => !shape.removed)
.map((shape) => shape.toJSON()),
.map((shape) => shape.toJSON() as SerializedShape),
tags: Object.values(this.tags)
.reduce((accumulator, frameTags) => {
accumulator.push(...frameTags);
Expand Down Expand Up @@ -331,7 +331,7 @@ export default class Collection {
}

if (object.shapeType === ShapeType.SKELETON) {
for (const element of (object as SkeletonShape | SkeletonTrack).elements) {
for (const element of (object as unknown as SkeletonShape | SkeletonTrack).elements) {
// for each track/shape element get its first objectState and keep it
elements[element.label.id] = [
...(elements[element.label.id] || []), element,
Expand Down
113 changes: 75 additions & 38 deletions cvat-core/src/annotations-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//
// SPDX-License-Identifier: MIT

import { omit } from 'lodash';
import config from './config';
import ObjectState, { SerializedData } from './object-state';
import { checkObjectType, clamp } from './common';
Expand Down Expand Up @@ -498,21 +499,27 @@ export class Shape extends Drawn {
public rotation: number;
public zOrder: number;

constructor(data: SerializedShape, clientID: number, color: string, injection: AnnotationInjection) {
constructor(
data: SerializedShape | SerializedShape['elements'][0],
clientID: number,
color: string,
injection: AnnotationInjection,
) {
super(data, clientID, color, injection);
this.points = data.points;
this.rotation = data.rotation || 0;
this.occluded = data.occluded;
this.outside = data.outside;
this.occluded = data.occluded || false;
this.outside = data.outside || false;
this.zOrder = data.z_order;
}

// Method is used to export data to the server
public toJSON(): SerializedShape {
public toJSON(): SerializedShape | SerializedShape['elements'][0] {
const result: SerializedShape = {
type: this.shapeType,
clientID: this.clientID,
occluded: this.occluded,
outside: this.outside,
z_order: this.zOrder,
points: this.points.slice(),
rotation: this.rotation,
Expand All @@ -535,8 +542,8 @@ export class Shape extends Drawn {
result.id = this.serverID;
}

if (typeof this.outside !== 'undefined') {
result.outside = this.outside;
if (this.parentID !== null) {
return omit(result, 'elements');
}

return result;
Expand Down Expand Up @@ -801,7 +808,12 @@ export interface InterpolatedPosition {

export class Track extends Drawn {
public shapes: Record<number, TrackedShape>;
constructor(data: SerializedTrack, clientID: number, color: string, injection: AnnotationInjection) {
constructor(
data: SerializedTrack | SerializedTrack['elements'][0],
clientID: number,
color: string,
injection: AnnotationInjection,
) {
super(data, clientID, color, injection);
this.shapes = data.shapes.reduce((shapeAccumulator, value) => {
shapeAccumulator[value.frame] = {
Expand All @@ -822,7 +834,7 @@ export class Track extends Drawn {
}

// Method is used to export data to the server
public toJSON(): SerializedTrack {
public toJSON(): SerializedTrack | SerializedTrack['elements'][0] {
const labelAttributes = attrsAsAnObject(this.label.attributes);

const result: SerializedTrack = {
Expand All @@ -831,7 +843,6 @@ export class Track extends Drawn {
frame: this.frame,
group: this.group,
source: this.source,
elements: [],
attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => {
if (!labelAttributes[attrId].mutable) {
attributeAccumulator.push({
Expand All @@ -842,6 +853,7 @@ export class Track extends Drawn {

return attributeAccumulator;
}, []),
elements: [],
shapes: Object.keys(this.shapes).reduce((shapesAccumulator, frame) => {
shapesAccumulator.push({
type: this.shapeType,
Expand Down Expand Up @@ -878,6 +890,10 @@ export class Track extends Drawn {
result.id = this.serverID;
}

if (this.parentID !== null) {
return omit(result, 'elements');
}

return result;
}

Expand Down Expand Up @@ -1552,7 +1568,12 @@ export class EllipseShape extends Shape {
}

class PolyShape extends Shape {
constructor(data: SerializedShape, clientID: number, color: string, injection: AnnotationInjection) {
constructor(
data: SerializedShape | SerializedShape['elements'][0],
clientID: number,
color: string,
injection: AnnotationInjection,
) {
super(data, clientID, color, injection);
this.rotation = 0; // is not supported
}
Expand Down Expand Up @@ -1674,7 +1695,12 @@ export class PolylineShape extends PolyShape {
}

export class PointsShape extends PolyShape {
constructor(data: SerializedShape, clientID: number, color: string, injection: AnnotationInjection) {
constructor(
data: SerializedShape | SerializedShape['elements'][0],
clientID: number,
color: string,
injection: AnnotationInjection,
) {
super(data, clientID, color, injection);
this.shapeType = ShapeType.POINTS;
checkNumberOfPoints(this.shapeType, this.points);
Expand Down Expand Up @@ -1841,7 +1867,6 @@ export class SkeletonShape extends Shape {
source: this.source,
rotation: 0,
frame: data.frame,
elements: [],
}, injection.nextClientID(), {
...injection,
parentID: this.clientID,
Expand Down Expand Up @@ -1892,7 +1917,6 @@ export class SkeletonShape extends Shape {
group: this.group,
z_order: this.zOrder,
rotation: 0,
elements: undefined,
}));

const result: SerializedShape = {
Expand All @@ -1901,7 +1925,7 @@ export class SkeletonShape extends Shape {
occluded: elements.every((el) => el.occluded),
outside: elements.every((el) => el.outside),
z_order: this.zOrder,
points: this.points,
points: [],
rotation: 0,
attributes: Object.keys(this.attributes).reduce((attributeAccumulator, attrId) => {
attributeAccumulator.push({
Expand Down Expand Up @@ -2123,7 +2147,7 @@ export class MaskShape extends Shape {
public bottom: number;
private getMasksOnFrame: AnnotationInjection['getMasksOnFrame'];

constructor(data, clientID, color, injection) {
constructor(data: SerializedShape, clientID: number, color: string, injection: AnnotationInjection) {
super(data, clientID, color, injection);
[this.left, this.top, this.right, this.bottom] = this.points.splice(-4, 4);
this.getMasksOnFrame = injection.getMasksOnFrame;
Expand Down Expand Up @@ -2324,7 +2348,12 @@ export class EllipseTrack extends Track {
}

class PolyTrack extends Track {
constructor(data: SerializedTrack, clientID: number, color: string, injection: AnnotationInjection) {
constructor(
data: SerializedTrack | SerializedTrack['elements'][0],
clientID: number,
color: string,
injection: AnnotationInjection,
) {
super(data, clientID, color, injection);
for (const shape of Object.values(this.shapes)) {
shape.rotation = 0; // is not supported
Expand Down Expand Up @@ -2634,7 +2663,12 @@ export class PolylineTrack extends PolyTrack {
}

export class PointsTrack extends PolyTrack {
constructor(data: SerializedTrack, clientID: number, color: string, injection: AnnotationInjection) {
constructor(
data: SerializedTrack | SerializedTrack['elements'][0],
clientID: number,
color: string,
injection: AnnotationInjection,
) {
super(data, clientID, color, injection);
this.shapeType = ShapeType.POINTS;
for (const shape of Object.values(this.shapes)) {
Expand Down Expand Up @@ -2730,11 +2764,6 @@ export class SkeletonTrack extends Track {
constructor(data: SerializedTrack, clientID: number, color: string, injection: AnnotationInjection) {
super(data, clientID, color, injection);
this.shapeType = ShapeType.SKELETON;

for (const shape of Object.values(this.shapes)) {
delete shape.points;
}

this.readOnlyFields = ['points', 'label', 'occluded', 'outside'];
this.pinned = false;
this.elements = data.elements.map((element: SerializedTrack['elements'][0]) => (
Expand Down Expand Up @@ -2837,10 +2866,10 @@ export class SkeletonTrack extends Track {
const result: SerializedTrack = Track.prototype.toJSON.call(this);
result.elements = this.elements.map((el) => ({
...el.toJSON(),
elements: undefined,
source: this.source,
group: this.group,
}));

result.elements.forEach((element) => {
element.shapes.forEach((shape) => {
shape.rotation = 0;
Expand Down Expand Up @@ -3085,35 +3114,39 @@ Object.defineProperty(EllipseTrack, 'distance', { value: EllipseShape.distance }
Object.defineProperty(CuboidTrack, 'distance', { value: CuboidShape.distance });
Object.defineProperty(SkeletonTrack, 'distance', { value: SkeletonShape.distance });

export function shapeFactory(data: SerializedShape, clientID: number, injection: AnnotationInjection): Shape {
export function shapeFactory(
data: SerializedShape | SerializedShape['elements'][0],
clientID: number,
injection: AnnotationInjection,
): Shape {
const { type } = data;
const color = colors[clientID % colors.length];

let shapeModel = null;
switch (type) {
case ShapeType.RECTANGLE:
shapeModel = new RectangleShape(data, clientID, color, injection);
shapeModel = new RectangleShape(data as SerializedShape, clientID, color, injection);
break;
case ShapeType.POLYGON:
shapeModel = new PolygonShape(data, clientID, color, injection);
shapeModel = new PolygonShape(data as SerializedShape, clientID, color, injection);
break;
case ShapeType.POLYLINE:
shapeModel = new PolylineShape(data, clientID, color, injection);
shapeModel = new PolylineShape(data as SerializedShape, clientID, color, injection);
break;
case ShapeType.POINTS:
shapeModel = new PointsShape(data, clientID, color, injection);
break;
case ShapeType.ELLIPSE:
shapeModel = new EllipseShape(data, clientID, color, injection);
shapeModel = new EllipseShape(data as SerializedShape, clientID, color, injection);
break;
case ShapeType.CUBOID:
shapeModel = new CuboidShape(data, clientID, color, injection);
shapeModel = new CuboidShape(data as SerializedShape, clientID, color, injection);
break;
case ShapeType.MASK:
shapeModel = new MaskShape(data, clientID, color, injection);
shapeModel = new MaskShape(data as SerializedShape, clientID, color, injection);
break;
case ShapeType.SKELETON:
shapeModel = new SkeletonShape(data, clientID, color, injection);
shapeModel = new SkeletonShape(data as SerializedShape, clientID, color, injection);
break;
default:
throw new DataError(`An unexpected type of shape "${type}"`);
Expand All @@ -3122,33 +3155,37 @@ export function shapeFactory(data: SerializedShape, clientID: number, injection:
return shapeModel;
}

export function trackFactory(trackData: SerializedTrack, clientID: number, injection: AnnotationInjection): Track {
export function trackFactory(
trackData: SerializedTrack | SerializedTrack['elements'][0],
clientID: number,
injection: AnnotationInjection,
): Track {
if (trackData.shapes.length) {
const { type } = trackData.shapes[0];
const color = colors[clientID % colors.length];

let trackModel = null;
switch (type) {
case ShapeType.RECTANGLE:
trackModel = new RectangleTrack(trackData, clientID, color, injection);
trackModel = new RectangleTrack(trackData as SerializedTrack, clientID, color, injection);
break;
case ShapeType.POLYGON:
trackModel = new PolygonTrack(trackData, clientID, color, injection);
trackModel = new PolygonTrack(trackData as SerializedTrack, clientID, color, injection);
break;
case ShapeType.POLYLINE:
trackModel = new PolylineTrack(trackData, clientID, color, injection);
trackModel = new PolylineTrack(trackData as SerializedTrack, clientID, color, injection);
break;
case ShapeType.POINTS:
trackModel = new PointsTrack(trackData, clientID, color, injection);
break;
case ShapeType.ELLIPSE:
trackModel = new EllipseTrack(trackData, clientID, color, injection);
trackModel = new EllipseTrack(trackData as SerializedTrack, clientID, color, injection);
break;
case ShapeType.CUBOID:
trackModel = new CuboidTrack(trackData, clientID, color, injection);
trackModel = new CuboidTrack(trackData as SerializedTrack, clientID, color, injection);
break;
case ShapeType.SKELETON:
trackModel = new SkeletonTrack(trackData, clientID, color, injection);
trackModel = new SkeletonTrack(trackData as SerializedTrack, clientID, color, injection);
break;
default:
throw new DataError(`An unexpected type of track "${type}"`);
Expand Down
Loading

0 comments on commit fb610a4

Please sign in to comment.