Skip to content

Commit

Permalink
Integration of REST/API in client #2 (#355)
Browse files Browse the repository at this point in the history
* Changed import/export
* Fixed attributes
* Parse annotation from the annotation view in a client
* Updated annotation upload in dashboard
* Updated browsing of a share
* Minimized version of jstree
* Fixed filenames from share
* Added jstree.min
* Removed legacy code
* Fixed typos
  • Loading branch information
bsekachev authored and nmanovic committed Mar 21, 2019
1 parent 26b44a5 commit 6ae5b9f
Show file tree
Hide file tree
Showing 24 changed files with 419 additions and 11,175 deletions.
8,589 changes: 0 additions & 8,589 deletions cvat/apps/dashboard/static/dashboard/js/3rdparty/jstree/jstree.js

This file was deleted.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.

This file was deleted.

Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
175 changes: 88 additions & 87 deletions cvat/apps/dashboard/static/dashboard/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,104 +81,91 @@ class TaskView {
});
}

_upload(tid) {
function parse(overlay, e) {
const xmlText = e.target.result;
$.get(`/api/v1/tasks/${tid}/frames/meta`).done((imageMetaCache) => {
const labelsCopy = JSON.parse(JSON.stringify(this._labels));
_upload() {
async function saveChunk(parsed, start, version) {
const CHUNK_SIZE = 100000;
let end = start + CHUNK_SIZE;
let chunk = {};
let next = false;

let chunk = {
shapes: [],
tracks: [],
tags: [],
version,
}

for (let prop in parsed) {
if (parsed.hasOwnProperty(prop)) {
chunk[prop] = parsed[prop].slice(start, end);
next |= chunk[prop].length > 0;
}
}

if (next) {
const response = await $.ajax({
url: `/api/v1/tasks/${this._id}/annotations`,
type: 'PATCH',
data: JSON.stringify(chunk),
contentType: 'application/json',
});

saveChunk.call(this, parsed, end, response.version);
}
}

async function save(parsed) {3
const response = await $.ajax({
url: `/api/v1/tasks/${this._id}/annotations`,
type: 'DELETE',
});

await saveChunk.call(this, parsed, 0, response.version);
}

async function onload(overlay, text) {
try {
overlay.setMessage('Required data are being downloaded from the server..');
const imageCache = await $.get(`/api/v1/tasks/${this._id}/frames/meta`);
const labelsCopy = JSON.parse(JSON.stringify(this._labels));
const parser = new AnnotationParser({
start: 0,
stop: this._size,
flipped: this._flipped,
image_meta_data: imageMetaCache,
image_meta_data: imageCache,
}, new LabelsInfo(labelsCopy));

function asyncParse() {
let parsed = null;
try {
parsed = parser.parse(xmlText);
}
catch(error) {
overlay.remove();
showMessage('Parsing errors occured. ' + error);
return;
}

function asyncSave() {
$.ajax({
// TODO: Use REST API
url: '/delete/annotation/task/' + window.cvat.dashboard.taskID,
type: 'DELETE',
success: function() {
asyncSaveChunk(0);
},
error: function(errorData) {
const message = `Could not remove current annotation. Code: ${errorData.status}. ` +
`Message: ${errorData.responseText || errorData.statusText}`;
showMessage(message);
},
});
};

function asyncSaveChunk(start) {
const CHUNK_SIZE = 100000;
let end = start + CHUNK_SIZE;
let chunk = {};
let next = false;
for (let prop in parsed) {
if (parsed.hasOwnProperty(prop)) {
chunk[prop] = parsed[prop].slice(start, end);
next |= chunk[prop].length > 0;
}
}
overlay.setMessage('The annotation file is being parsed..');
const parsed = parser.parse(text);

if (next) {
const exportData = createExportContainer();
exportData.create = chunk;

$.ajax({
// TODO: Use REST API
url: `/save/annotation/task/${tid}`,
type: 'POST',
data: JSON.stringify(exportData),
contentType: 'application/json',
}).done(() => {
asyncSaveChunk(end);
}).fail((errorData) => {
const message = `Annotation uploading errors occurred. Code: ${errorData.status}. ` +
`Message: ${errorData.responseText || errorData.statusText}`;
showMessage(message);
});
} else {
const message = 'Annotation have been successfully uploaded';
showMessage(message);
overlay.remove();
}
}
overlay.setMessage('The annotation is being saved..');
await save.call(this, parsed);

overlay.setMessage('The annotation is being saved..');
setTimeout(asyncSave);
const message = 'Annotation have been successfully uploaded';
showMessage(message);
} catch(errorData) {
let message = null;
if (typeof(errorData) === 'string') {
message = `Can not upload annotations. ${errorData}`;
} else {
message = `Can not upload annotations. Code: ${errorData.status}. ` +
`Message: ${errorData.responseText || errorData.statusText}`;
}

overlay.setMessage('The annotation file is being parsed..');
setTimeout(asyncParse);
}).fail((errorData) => {
const message = `Can not get required data from the server. Code: ${errorData.status}. ` +
`Message: ${errorData.responseText || errorData.statusText}`;
showMessage(message);
});

overlay.setMessage('Required data are being downloaded from the server..');
} finally {
overlay.remove();
}
}

self = this;
$('<input type="file" accept="text/xml">').on('change', function() {
const file = this.files[0];
$(this).remove();
$('<input type="file" accept="text/xml">').on('change', (e) => {
const file = e.target.files[0];
$(e.target).remove();
if (file) {
const overlay = showOverlay('File is being parsed..');
const fileReader = new FileReader();
fileReader.onload = parse.bind(self, overlay);
fileReader.onload = (e) => {
onload.call(this, overlay, e.target.result);
}
fileReader.readAsText(file);
}
}).click();
Expand Down Expand Up @@ -473,9 +460,23 @@ class DashboardView {
shareFileSelector.removeClass('hidden');
shareBrowseTree.jstree({
core: {
data: {
url: 'get_share_nodes',
data: (node) => { return {'id' : node.id}; }
data: async function (obj, callback) {
let url = '/api/v1/server/share';

if (obj.id != '#') {
url += `?directory=${obj.id.substr(2)}`;
}

const response = await $.get(url);
const files = Array.from(response, (element) => {
return {
id: `${obj.id}/${element.name}`,
children: element.type === 'DIR',
text: element.name}
}
);

callback.call(this, files);
}
},
plugins: ['checkbox', 'sort'],
Expand All @@ -491,7 +492,7 @@ class DashboardView {
cancelBrowseServer.on('click', () => shareFileSelector.addClass('hidden'));
submitBrowseServer.on('click', () => {
if (!createModal.hasClass('hidden')) {
files = shareBrowseTree.jstree(true).get_selected();
files = Array.from(shareBrowseTree.jstree(true).get_selected(), (el) => el.substr(2));
cancelBrowseServer.click();
updateSelectedFiles();
}
Expand Down
4 changes: 2 additions & 2 deletions cvat/apps/dashboard/templates/dashboard/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{% block head_css %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'dashboard/stylesheet.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dashboard/js/3rdparty/jstree/themes/default/style.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dashboard/js/3rdparty/jstree/themes/default/style.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dashboard/js/3rdparty/pagination/pagination.css' %}">
{% for css_file in css_3rdparty %}
<link rel="stylesheet" type="text/css" href="{% static css_file %}">
Expand All @@ -24,7 +24,7 @@

{% block head_js_3rdparty %}
{{ block.super }}
<script type="text/javascript" src="{% static 'dashboard/js/3rdparty/jstree/jstree.js' %}"></script>
<script type="text/javascript" src="{% static 'dashboard/js/3rdparty/jstree/jstree.min.js' %}"></script>
<script type="text/javascript" src="{% static 'dashboard/js/3rdparty/pagination/pagination.min.js' %}"></script>
{% for js_file in js_3rdparty %}
<script type="text/javascript" src="{% static js_file %}" defer></script>
Expand Down
1 change: 0 additions & 1 deletion cvat/apps/dashboard/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from . import views

urlpatterns = [
path('get_share_nodes', views.JsTreeView),
path('', views.DashboardView),
path('meta', views.DashboardMeta),
]
Expand Down
39 changes: 0 additions & 39 deletions cvat/apps/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,6 @@

import os

def ScanNode(directory):
if '..' in directory.split(os.path.sep):
return HttpResponseBadRequest('Permission Denied')

act_dir = os.path.normpath(settings.SHARE_ROOT + directory)
result = []

nodes = os.listdir(act_dir)
files = filter(os.path.isfile, map(lambda f: os.path.join(act_dir, f), nodes))
dirs = filter(os.path.isdir, map(lambda d: os.path.join(act_dir, d), nodes))

for d in dirs:
name = os.path.basename(d)
children = len(os.listdir(d)) > 0
node = {'id': directory + name + '/', 'text': name, 'children': children}
result.append(node)

for f in files:
name = os.path.basename(f)
node = {'id': directory + name, 'text': name, "icon" : "jstree-file"}
result.append(node)

return result

@login_required
def JsTreeView(request):
node_id = None
if 'id' in request.GET:
node_id = request.GET['id']

if node_id is None or node_id == '#':
node_id = '/'
response = [{"id": node_id, "text": node_id, "children": ScanNode(node_id)}]
else:
response = ScanNode(node_id)

return JsonResponse(response, safe=False,
json_dumps_params=dict(ensure_ascii=False))

@login_required
def DashboardView(request):
return render(request, 'dashboard/dashboard.html', {
Expand Down
Loading

0 comments on commit 6ae5b9f

Please sign in to comment.