From 920247dc31811a0bda20aa66bf86c0c9e9f86ace Mon Sep 17 00:00:00 2001 From: Boris Sekachev <40690378+bsekachev@users.noreply.github.com> Date: Wed, 24 Oct 2018 19:19:04 +0300 Subject: [PATCH] Job status was implemented (#153) --- .../migrations/0012_auto_20181024_1817.py | 23 ++++++++++++++++ cvat/apps/engine/models.py | 19 +++++++++++-- .../engine/static/engine/js/annotationUI.js | 16 +++++++++-- cvat/apps/engine/task.py | 27 +++++++++++++++++-- .../engine/templates/engine/annotation.html | 12 +++++++-- cvat/apps/engine/urls.py | 3 ++- cvat/apps/engine/views.py | 21 ++++++++++++++- 7 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 cvat/apps/engine/migrations/0012_auto_20181024_1817.py diff --git a/cvat/apps/engine/migrations/0012_auto_20181024_1817.py b/cvat/apps/engine/migrations/0012_auto_20181024_1817.py new file mode 100644 index 000000000000..b4ea6627adc3 --- /dev/null +++ b/cvat/apps/engine/migrations/0012_auto_20181024_1817.py @@ -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), + ), + ] diff --git a/cvat/apps/engine/models.py b/cvat/apps/engine/models.py index 3e16f8fa3969..dafe1da6a5d1 100644 --- a/cvat/apps/engine/models.py +++ b/cvat/apps/engine/models.py @@ -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) @@ -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): diff --git a/cvat/apps/engine/static/engine/js/annotationUI.js b/cvat/apps/engine/static/engine/js/annotationUI.js index 9a80c51cd3ca..6183b7710cd4 100644 --- a/cvat/apps/engine/static/engine/js/annotationUI.js +++ b/cvat/apps/engine/static/engine/js/annotationUI.js @@ -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', () => { diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py index c8e165b9165b..84381ced2851 100644 --- a/cvat/apps/engine/task.py +++ b/cvat/apps/engine/task.py @@ -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 @@ -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, diff --git a/cvat/apps/engine/templates/engine/annotation.html b/cvat/apps/engine/templates/engine/annotation.html index fcdbc4260426..a72bd4db4f71 100644 --- a/cvat/apps/engine/templates/engine/annotation.html +++ b/cvat/apps/engine/templates/engine/annotation.html @@ -306,7 +306,9 @@ -