提交 920247dc 编写于 作者: B Boris Sekachev 提交者: Nikita Manovich

Job status was implemented (#153)

上级 89234496
# 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),
),
]
......@@ -8,12 +8,26 @@ from django.conf import settings
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)
......@@ -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:
......@@ -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):
......
......@@ -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', () => {
......
......@@ -18,6 +18,8 @@ _SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__))
_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
......@@ -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
......@@ -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)
......@@ -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,
......
......@@ -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>
......@@ -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%">
......
......@@ -22,5 +22,6 @@ urlpatterns = [
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),
]
......@@ -20,6 +20,7 @@ from cvat.apps.authentication.decorators import login_required
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
......@@ -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/')
......@@ -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}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册