Skip to content

Commit

Permalink
Job status was implemented (#153)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekachev authored and nmanovic committed Oct 24, 2018
1 parent 8923449 commit 920247d
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 10 deletions.
23 changes: 23 additions & 0 deletions cvat/apps/engine/migrations/0012_auto_20181024_1817.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 2.0.9 on 2018-10-24 15:17

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('engine', '0011_add_task_source_and_safecharfield'),
]

operations = [
migrations.AddField(
model_name='job',
name='status',
field=models.CharField(default='annotation', max_length=32),
),
migrations.AlterField(
model_name='task',
name='status',
field=models.CharField(default='annotation', max_length=32),
),
]
19 changes: 17 additions & 2 deletions cvat/apps/engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,26 @@

from django.contrib.auth.models import User

from io import StringIO
from enum import Enum

import shlex
import csv
from io import StringIO
import re
import os

class StatusChoice(Enum):
ANNOTATION = 'annotation'
VALIDATION = 'validation'
COMPLETED = 'completed'

@classmethod
def choices(self):
return tuple((x.name, x.value) for x in self)

def __str__(self):
return self.value

class SafeCharField(models.CharField):
def get_prep_value(self, value):
value = super().get_prep_value(value)
Expand All @@ -30,11 +44,11 @@ class Task(models.Model):
bug_tracker = models.CharField(max_length=2000, default="")
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=32, default="annotate")
overlap = models.PositiveIntegerField(default=0)
z_order = models.BooleanField(default=False)
flipped = models.BooleanField(default=False)
source = SafeCharField(max_length=256, default="unknown")
status = models.CharField(max_length=32, default=StatusChoice.ANNOTATION)

# Extend default permission model
class Meta:
Expand Down Expand Up @@ -81,6 +95,7 @@ class Segment(models.Model):
class Job(models.Model):
segment = models.ForeignKey(Segment, on_delete=models.CASCADE)
annotator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
status = models.CharField(max_length=32, default=StatusChoice.ANNOTATION)
# TODO: add sub-issue number for the task

class Label(models.Model):
Expand Down
16 changes: 14 additions & 2 deletions cvat/apps/engine/static/engine/js/annotationUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,12 +512,24 @@ function setupMenu(job, shapeCollectionModel, annotationParser, aamModel, player
})();

$('#statTaskName').text(job.slug);
$('#statTaskStatus').text(job.status);
$('#statFrames').text(`[${job.start}-${job.stop}]`);
$('#statOverlap').text(job.overlap);
$('#statZOrder').text(job.z_order);
$('#statFlipped').text(job.flipped);

$('#statTaskStatus').prop("value", job.status).on('change', (e) => {
$.ajax({
type: 'POST',
url: 'save/job/status',
data: JSON.stringify({
jid: window.cvat.job.id,
status: e.target.value
}),
contentType: "application/json; charset=utf-8",
error: (data) => {
showMessage(`Can not change job status. Code: ${data.status}. Message: ${data.responeText || data.statusText}`);
}
});
});

let shortkeys = window.cvat.config.shortkeys;
$('#helpButton').on('click', () => {
Expand Down
27 changes: 25 additions & 2 deletions cvat/apps/engine/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
_MEDIA_MIMETYPES_FILE = os.path.join(_SCRIPT_DIR, "media.mimetypes")
mimetypes.init(files=[_MEDIA_MIMETYPES_FILE])

from cvat.apps.engine.models import StatusChoice

import django_rq
from django.conf import settings
from django.db import transaction
Expand Down Expand Up @@ -164,7 +166,7 @@ def get(tid):
job_indexes = [segment.job_set.first().id for segment in db_segments]

response = {
"status": db_task.status.capitalize(),
"status": db_task.status,
"spec": {
"labels": { db_label.id:db_label.name for db_label in db_labels },
"attributes": attributes
Expand All @@ -185,6 +187,27 @@ def get(tid):

return response

def save_job_status(jid, status, user):
db_job = models.Job.objects.select_related("segment__task").select_for_update().get(pk = jid)
db_task = db_job.segment.task
status = StatusChoice(status)

slogger.job[jid].info('changing job status from {} to {} by an user {}'.format(db_job.status, status, user))

db_job.status = status.value
db_job.save()
db_segments = list(db_task.segment_set.prefetch_related('job_set').select_for_update().all())
db_jobs = [db_segment.job_set.first() for db_segment in db_segments]

if len(list(filter(lambda x: StatusChoice(x.status) == StatusChoice.ANNOTATION, db_jobs))) > 0:
db_task.status = StatusChoice.ANNOTATION
elif len(list(filter(lambda x: StatusChoice(x.status) == StatusChoice.VALIDATION, db_jobs))) > 0:
db_task.status = StatusChoice.VALIDATION
else:
db_task.status = StatusChoice.COMPLETED

db_task.save()

def get_job(jid):
"""Get the job as dictionary of attributes"""
db_job = models.Job.objects.select_related("segment__task").get(id=jid)
Expand All @@ -205,7 +228,7 @@ def get_job(jid):
attributes[db_label.id][db_attrspec.id] = db_attrspec.text

response = {
"status": db_task.status.capitalize(),
"status": db_job.status,
"labels": { db_label.id:db_label.name for db_label in db_labels },
"stop": db_segment.stop_frame,
"taskid": db_task.id,
Expand Down
12 changes: 10 additions & 2 deletions cvat/apps/engine/templates/engine/annotation.html
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,9 @@
</table>
</div>

<center> <button id="closeSettignsButton" class="regular h1" style="margin-top: 15px;"> Close </button> </center>
<center>
<button id="closeSettignsButton" class="regular h1" style="margin-top: 15px;"> Close </button>
</center>
</div>
</div>

Expand All @@ -325,7 +327,13 @@
<div style="float:left; width: 70%; height: 100%; text-align: left;" class="selectable">
<center>
<label id="statTaskName" class="semiBold h2"> </label> <br>
<label id="statTaskStatus" class="regular h2"> </label>
<center>
<select id="statTaskStatus" class="regular h2" style="outline: none; border-radius: 10px; background:#B0C4DE; border: 1px solid black;">
{% for status in status_list %}
<option value="{{status}}"> {{status}} </option>
{% endfor %}
</select>
</center>
</center>
<center>
<table style="width: 100%">
Expand Down
3 changes: 2 additions & 1 deletion cvat/apps/engine/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
path('save/annotation/task/<int:tid>', views.save_annotation_for_task),
path('get/annotation/job/<int:jid>', views.get_annotation),
path('get/username', views.get_username),
path('save/exception/<int:jid>', views.catch_client_exception)
path('save/exception/<int:jid>', views.catch_client_exception),
path('save/job/status', views.save_job_status),
]
21 changes: 20 additions & 1 deletion cvat/apps/engine/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from requests.exceptions import RequestException
import logging
from .log import slogger, clogger
from cvat.apps.engine.models import StatusChoice

############################# High Level server API
@login_required
Expand All @@ -36,7 +37,8 @@ def dispatch_request(request):
"""An entry point to dispatch legacy requests"""
if request.method == 'GET' and 'id' in request.GET:
return render(request, 'engine/annotation.html', {
'js_3rdparty': JS_3RDPARTY.get('engine', [])
'js_3rdparty': JS_3RDPARTY.get('engine', []),
'status_list': [str(i) for i in StatusChoice]
})
else:
return redirect('/dashboard/')
Expand Down Expand Up @@ -273,6 +275,23 @@ def save_annotation_for_task(request, tid):

return HttpResponse()

@login_required
@permission_required(perm=['engine.view_task', 'engine.change_task'], raise_exception=True)
def save_job_status(request):
try:
data = json.loads(request.body.decode('utf-8'))
jid = data['jid']
status = data['status']
slogger.job[jid].info("changing job status request")
task.save_job_status(jid, status, request.user.username)
except Exception as e:
if jid:
slogger.job[jid].error("cannot change status", exc_info=True)
else:
slogger.glob.error("cannot change status", exc_info=True)
return HttpResponseBadRequest(str(e))
return HttpResponse()

@login_required
def get_username(request):
response = {'username': request.user.username}
Expand Down

0 comments on commit 920247d

Please sign in to comment.