Skip to content

Commit

Permalink
Rest API dump & save fixes (#409)
Browse files Browse the repository at this point in the history
* Some client saving fixes
* Dump fixes
* Added empty attributes
* Couple of comments
  • Loading branch information
bsekachev authored and nmanovic committed Apr 18, 2019
1 parent a01555e commit 2b3e59e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 12 deletions.
25 changes: 18 additions & 7 deletions cvat/apps/engine/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class dotdict(OrderedDict):

for key in keys_for_merge:
item = dotdict({v.split('__', 1)[-1]:row[v] for v in keys_for_merge[key]})
if item.id:
if item.id is not None:
merged_rows[row_id][key].append(item)

# Remove redundant keys from final objects
Expand Down Expand Up @@ -556,6 +556,14 @@ def _init_tracks_from_db(self):
]
}, 'id')

# A result table can consist many equal rows for track/shape attributes
# We need filter unique attributes manually
db_track["labeledtrackattributeval_set"] = list(set(db_track["labeledtrackattributeval_set"]))
for db_shape in db_track["trackedshape_set"]:
db_shape["trackedshapeattributeval_set"] = list(
set(db_shape["trackedshapeattributeval_set"])
)

serializer = serializers.LabeledTrackSerializer(db_tracks, many=True)
self.data["tracks"] = serializer.data

Expand Down Expand Up @@ -924,8 +932,9 @@ def to_tracks(self):
shape0 = copy.copy(shape)
shape0["keyframe"] = True
shape0["outside"] = False
# TODO: Separate attributes on mutable and unmutable
shape0["attributes"] = []
shape0.pop("group", None)
shape0.pop("attributes")
shape1 = copy.copy(shape0)
shape1["outside"] = True
shape1["frame"] += 1
Expand Down Expand Up @@ -1099,15 +1108,17 @@ def interpolate(shape0, shape1):
curr_frame = track["shapes"][0]["frame"]
prev_shape = {}
for shape in track["shapes"]:
if shape["frame"] != curr_frame:
if prev_shape:
assert shape["frame"] > curr_frame
for attr in prev_shape["attributes"]:
if attr["spec_id"] not in map(lambda el: el["spec_id"], shape["attributes"]):
shape["attributes"].append(copy.deepcopy(attr))
if not prev_shape["outside"]:
shapes.extend(interpolate(prev_shape, shape))

if not shape["outside"]:
shape["keyframe"] = True
shapes.append(shape)
curr_frame = shape["frame"] + 1
shape["keyframe"] = True
shapes.append(shape)
curr_frame = shape["frame"]
prev_shape = shape

# TODO: Need to modify a client and a database (append "outside" shapes for polytracks)
Expand Down
20 changes: 15 additions & 5 deletions cvat/apps/engine/static/engine/js/annotationSaver.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ class AnnotationSaverModel extends Listener {

this._hash = this._getHash();

for (const shape of initialData.shapes) {
// We need use data from export instead of initialData
// Otherwise we have differ keys order and JSON comparison code incorrect
const data = this._shapeCollection.export()[0];
for (const shape of data.shapes) {
this._initialObjects[shape.id] = shape;
}

for (const track of initialData.tracks) {
for (const track of data.tracks) {
this._initialObjects[track.id] = track;
}
}
Expand Down Expand Up @@ -177,6 +180,7 @@ class AnnotationSaverModel extends Listener {
}

_updateCreatedObjects(objectsToSave, savedObjects, mapping) {
// Method setups IDs of created objects after saving on a server
const allSavedObjects = savedObjects.shapes.concat(savedObjects.tracks);
const allObjectsToSave = objectsToSave.shapes.concat(objectsToSave.tracks);
if (allSavedObjects.length !== allObjectsToSave.length) {
Expand All @@ -185,7 +189,9 @@ class AnnotationSaverModel extends Listener {

for (let idx = 0; idx < allSavedObjects.length; idx += 1) {
const objectModel = mapping.filter(el => el[0] === allObjectsToSave[idx])[0][1];
objectModel.serverID = allSavedObjects[idx].id;
const { id } = allSavedObjects[idx];
objectModel.serverID = id;
allObjectsToSave[idx].id = id;
}

this._shapeCollection.update();
Expand Down Expand Up @@ -218,6 +224,7 @@ class AnnotationSaverModel extends Listener {
const savedObjects = await this._put(data);
this._updateCreatedObjects(exported, savedObjects, mapping);
this._shapeCollection.flush = false;
this._version = savedObjects.version;
for (const object of savedObjects.shapes.concat(savedObjects.tracks)) {
this._initialObjects[object.id] = object;
}
Expand All @@ -228,20 +235,23 @@ class AnnotationSaverModel extends Listener {
this.notify('saveCreated');
const savedCreated = await this._create(created);
this._updateCreatedObjects(created, savedCreated, mapping);
for (const object of savedCreated.shapes.concat(savedCreated.tracks)) {
this._version = savedCreated.version;
for (const object of created.shapes.concat(created.tracks)) {
this._initialObjects[object.id] = object;
}

this.notify('saveUpdated');
const savedUpdated = await this._update(updated);
for (const object of savedUpdated.shapes.concat(savedUpdated.tracks)) {
this._version = savedUpdated.version;
for (const object of updated.shapes.concat(updated.tracks)) {
if (object.id in this._initialObjects) {
this._initialObjects[object.id] = object;
}
}

this.notify('saveDeleted');
const savedDeleted = await this._delete(deleted);
this._version = savedDeleted.version;
for (const object of savedDeleted.shapes.concat(savedDeleted.tracks)) {
if (object.id in this._initialObjects) {
delete this._initialObjects[object.id];
Expand Down

0 comments on commit 2b3e59e

Please sign in to comment.