diff --git a/cvat-core/src/annotations.js b/cvat-core/src/annotations.js
index 8c9014caf52d..a892512c2811 100644
--- a/cvat-core/src/annotations.js
+++ b/cvat-core/src/annotations.js
@@ -225,6 +225,25 @@
return result;
}
+ async function exportDataset(session, format) {
+ if (!(format instanceof String || typeof format === 'string')) {
+ throw new ArgumentError(
+ 'Format must be a string',
+ );
+ }
+ if (!(session instanceof Task)) {
+ throw new ArgumentError(
+ 'A dataset can only be created from a task',
+ );
+ }
+
+ let result = null;
+ result = await serverProxy.tasks
+ .exportDataset(session.id, format);
+
+ return result;
+ }
+
module.exports = {
getAnnotations,
putAnnotations,
@@ -238,5 +257,6 @@
selectObject,
uploadAnnotations,
dumpAnnotations,
+ exportDataset,
};
})();
diff --git a/cvat-core/src/api-implementation.js b/cvat-core/src/api-implementation.js
index 72213c942a0f..2aef836ddb5e 100644
--- a/cvat-core/src/api-implementation.js
+++ b/cvat-core/src/api-implementation.js
@@ -70,6 +70,11 @@
return result.map((el) => new AnnotationFormat(el));
};
+ cvat.server.datasetFormats.implementation = async () => {
+ const result = await serverProxy.server.datasetFormats();
+ return result;
+ };
+
cvat.server.register.implementation = async (username, firstName, lastName,
email, password1, password2) => {
await serverProxy.server.register(username, firstName, lastName, email,
diff --git a/cvat-core/src/api.js b/cvat-core/src/api.js
index 21c2299a2493..51d07627fa76 100644
--- a/cvat-core/src/api.js
+++ b/cvat-core/src/api.js
@@ -115,6 +115,20 @@ function build() {
.apiWrapper(cvat.server.formats);
return result;
},
+ /**
+ * Method returns available dataset export formats
+ * @method exportFormats
+ * @async
+ * @memberof module:API.cvat.server
+ * @returns {module:String[]}
+ * @throws {module:API.cvat.exceptions.PluginError}
+ * @throws {module:API.cvat.exceptions.ServerError}
+ */
+ async datasetFormats() {
+ const result = await PluginRegistry
+ .apiWrapper(cvat.server.datasetFormats);
+ return result;
+ },
/**
* Method allows to register on a server
* @method register
diff --git a/cvat-core/src/server-proxy.js b/cvat-core/src/server-proxy.js
index 143cb8e16592..aa9982347fe6 100644
--- a/cvat-core/src/server-proxy.js
+++ b/cvat-core/src/server-proxy.js
@@ -101,6 +101,22 @@
return response.data;
}
+ async function datasetFormats() {
+ const { backendAPI } = config;
+
+ let response = null;
+ try {
+ response = await Axios.get(`${backendAPI}/server/dataset/formats`, {
+ proxy: config.proxy,
+ });
+ response = JSON.parse(response.data);
+ } catch (errorData) {
+ throw generateError(errorData, 'Could not get export formats from the server');
+ }
+
+ return response;
+ }
+
async function register(username, firstName, lastName, email, password1, password2) {
let response = null;
try {
@@ -223,6 +239,35 @@
}
}
+ async function exportDataset(id, format) {
+ const { backendAPI } = config;
+ let url = `${backendAPI}/tasks/${id}/dataset?format=${format}`;
+
+ return new Promise((resolve, reject) => {
+ async function request() {
+ try {
+ const response = await Axios
+ .get(`${url}`, {
+ proxy: config.proxy,
+ });
+ if (response.status === 202) {
+ setTimeout(request, 3000);
+ } else {
+ url = `${url}&action=download`;
+ resolve(url);
+ }
+ } catch (errorData) {
+ reject(generateError(
+ errorData,
+ `Failed to export the task ${id} as a dataset`,
+ ));
+ }
+ }
+
+ setTimeout(request);
+ });
+ }
+
async function createTask(taskData, files, onUpdate) {
const { backendAPI } = config;
@@ -555,6 +600,7 @@
about,
share,
formats,
+ datasetFormats,
exception,
login,
logout,
@@ -570,6 +616,7 @@
saveTask,
createTask,
deleteTask,
+ exportDataset,
}),
writable: false,
},
diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js
index aa681c0e3059..55ff05b35d1b 100644
--- a/cvat-core/src/session.js
+++ b/cvat-core/src/session.js
@@ -100,6 +100,12 @@
objectStates, reset);
return result;
},
+
+ async exportDataset(format) {
+ const result = await PluginRegistry
+ .apiWrapper.call(this, prototype.annotations.exportDataset, format);
+ return result;
+ },
},
writable: true,
}),
@@ -367,6 +373,19 @@
* @instance
* @async
*/
+ /**
+ * Export as a dataset.
+ * Method builds a dataset in the specified format.
+ * @method exportDataset
+ * @memberof Session.annotations
+ * @param {module:String} format - a format
+ * @returns {string} An URL to the dataset file
+ * @throws {module:API.cvat.exceptions.PluginError}
+ * @throws {module:API.cvat.exceptions.ServerError}
+ * @throws {module:API.cvat.exceptions.ArgumentError}
+ * @instance
+ * @async
+ */
/**
@@ -1132,6 +1151,8 @@
statistics: Object.getPrototypeOf(this).annotations.statistics.bind(this),
hasUnsavedChanges: Object.getPrototypeOf(this)
.annotations.hasUnsavedChanges.bind(this),
+ exportDataset: Object.getPrototypeOf(this)
+ .annotations.exportDataset.bind(this),
};
this.frames = {
@@ -1195,6 +1216,7 @@
annotationsStatistics,
uploadAnnotations,
dumpAnnotations,
+ exportDataset,
} = require('./annotations');
buildDublicatedAPI(Job.prototype);
@@ -1457,4 +1479,9 @@
const result = await dumpAnnotations(this, name, dumper);
return result;
};
+
+ Task.prototype.annotations.exportDataset.implementation = async function (format) {
+ const result = await exportDataset(this, format);
+ return result;
+ };
})();
diff --git a/cvat/apps/dashboard/static/dashboard/js/dashboard.js b/cvat/apps/dashboard/static/dashboard/js/dashboard.js
index 8ebf05ea934c..bd9e6bb5de30 100644
--- a/cvat/apps/dashboard/static/dashboard/js/dashboard.js
+++ b/cvat/apps/dashboard/static/dashboard/js/dashboard.js
@@ -13,9 +13,10 @@
*/
class TaskView {
- constructor(task, annotationFormats) {
+ constructor(task, annotationFormats, exportFormats) {
this.init(task);
this._annotationFormats = annotationFormats;
+ this._exportFormats = exportFormats;
this._UI = null;
}
@@ -109,6 +110,28 @@ class TaskView {
}
}
+ async _exportDataset(button, formatName) {
+ button.disabled = true;
+ try {
+ const format = this._exportFormats.find((x) => {
+ return x.name == formatName;
+ });
+ if (!format) {
+ throw `Unknown dataset export format '${formatName}'`;
+ }
+ const url = await this._task.annotations.exportDataset(format.tag);
+ const tempElem = document.createElement('a');
+ tempElem.href = `${url}`;
+ document.body.appendChild(tempElem);
+ tempElem.click();
+ tempElem.remove();
+ } catch (error) {
+ showMessage(error.message);
+ } finally {
+ button.disabled = false;
+ }
+ }
+
init(task) {
this._task = task;
}
@@ -169,6 +192,22 @@ class TaskView {
downloadButton.appendTo(buttonsContainer);
uploadButton.appendTo(buttonsContainer);
+ const exportButton = $('');
+ $('').appendTo(exportButton);
+ for (const format of this._exportFormats) {
+ const item = $(`