From c6b806071fda4e65b19070c3ea48a9cc5d61c9ce Mon Sep 17 00:00:00 2001 From: Nikita Manovich Date: Tue, 5 Feb 2019 22:37:58 +0300 Subject: [PATCH] Minor changes inside server. --- .../engine/migrations/0024_pluginoption.py | 24 +++++++ cvat/apps/engine/models.py | 36 ++++++----- cvat/apps/engine/serializers.py | 15 ++--- cvat/apps/engine/task.py | 2 +- cvat/apps/engine/urls.py | 2 +- cvat/apps/engine/views.py | 63 ------------------- 6 files changed, 56 insertions(+), 86 deletions(-) create mode 100644 cvat/apps/engine/migrations/0024_pluginoption.py diff --git a/cvat/apps/engine/migrations/0024_pluginoption.py b/cvat/apps/engine/migrations/0024_pluginoption.py new file mode 100644 index 000000000000..997546fd0050 --- /dev/null +++ b/cvat/apps/engine/migrations/0024_pluginoption.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.5 on 2019-02-05 18:17 + +import cvat.apps.engine.models +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('engine', '0023_plugin'), + ] + + operations = [ + migrations.CreateModel( + name='PluginOption', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', cvat.apps.engine.models.SafeCharField(max_length=32)), + ('value', cvat.apps.engine.models.SafeCharField(max_length=1024)), + ('plugin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='engine.Plugin')), + ], + ), + ] diff --git a/cvat/apps/engine/models.py b/cvat/apps/engine/models.py index 5a0c55881834..a22999221263 100644 --- a/cvat/apps/engine/models.py +++ b/cvat/apps/engine/models.py @@ -139,29 +139,37 @@ def __str__(self): class Meta: default_permissions = () + unique_together = ('task', 'name') - -def parse_attribute(text): - match = re.match(r'^([~@])(\w+)=(\w+):(.+)?$', text) - prefix = match.group(1) - type = match.group(2) - name = match.group(3) - if match.group(4): - values = list(csv.reader(StringIO(match.group(4)), quotechar="'"))[0] - else: - values = [] - - return {'prefix':prefix, 'type':type, 'name':name, 'values':values} - +# FIXME: need to remote text and add (name, type, permanent, +# default_value, values) class AttributeSpec(models.Model): label = models.ForeignKey(Label, on_delete=models.CASCADE) text = models.CharField(max_length=1024) class Meta: default_permissions = () + # FIXME: unique_together = ('label', 'name') + + @classmethod + def parse(cls, value): + match = re.match(r'^([~@])(\w+)=(\w+):(.+)?$', value) + if match: + prefix = match.group(1) + type = match.group(2) + name = match.group(3) + if match.group(4): + values = list(csv.reader(StringIO(match.group(4)), + quotechar="'"))[0] + else: + values = [] + + return {'prefix':prefix, 'type':type, 'name':name, 'values':values} + else: + return None def get_attribute(self): - return parse_attribute(self.text) + return self.parse(self.text) def is_mutable(self): attr = self.get_attribute() diff --git a/cvat/apps/engine/serializers.py b/cvat/apps/engine/serializers.py index 509d7c8a11ef..37cf6bd160ba 100644 --- a/cvat/apps/engine/serializers.py +++ b/cvat/apps/engine/serializers.py @@ -16,6 +16,14 @@ class Meta: model = AttributeSpec fields = ('id', 'text') + def validate_text(self, value): + attr = AttributeSpec.parse(value) + if attr is None: + message = "{} attribute value isn't correct".format(value) + raise serializers.ValidationError(message) + + return value + class LabelSerializer(serializers.ModelSerializer): attributes = AttributeSerializer(many=True, source='attributespec_set', default=[]) @@ -23,13 +31,6 @@ class Meta: model = Label fields = ('id', 'name', 'attributes') - # When data is a part of multipart/form-data need to convert labels from - # json string to the internal representation. - def to_internal_value(self, data): - if isinstance(data, str): - data = json.loads(data) - return super().to_internal_value(data) - class JobSerializer(serializers.ModelSerializer): task_id = serializers.ReadOnlyField(source="segment.task.id") start_frame = serializers.ReadOnlyField(source="segment.start_frame") diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py index 8a27c4501423..7ad862be16ea 100644 --- a/cvat/apps/engine/task.py +++ b/cvat/apps/engine/task.py @@ -386,7 +386,7 @@ def _parse_labels(labels): parsed_labels[token] = {} last_label = token else: - attr = models.parse_attribute(token) + attr = models.AttributeSpec.parse(token) attr['text'] = token if not attr['type'] in ['checkbox', 'radio', 'number', 'text', 'select']: raise ValueError("labels string is not corect. " + diff --git a/cvat/apps/engine/urls.py b/cvat/apps/engine/urls.py index 481d1243725c..d7786a4a1275 100644 --- a/cvat/apps/engine/urls.py +++ b/cvat/apps/engine/urls.py @@ -24,7 +24,7 @@ # deprecated API path('', views.dispatch_request), - path('create/task', views.create_task), + #path('create/task', views.create_task), #path('get/task//frame/', views.get_frame), path('check/task/', views.check_task), path('delete/task/', views.delete_task), diff --git a/cvat/apps/engine/views.py b/cvat/apps/engine/views.py index 8dca9bf2d0f0..9180e99ef32a 100644 --- a/cvat/apps/engine/views.py +++ b/cvat/apps/engine/views.py @@ -254,69 +254,6 @@ def dispatch_request(request): else: return redirect('/dashboard/') - -@login_required -@permission_required(perm=['engine.task.create'], raise_exception=True) -def create_task(request): - """Create a new annotation task""" - - db_task = None - params = request.POST.dict() - params['owner'] = request.user - slogger.glob.info("create task with params = {}".format(params)) - try: - db_task = task.create_empty(params) - target_paths = [] - source_paths = [] - upload_dir = db_task.get_upload_dirname() - share_root = settings.SHARE_ROOT - if params['storage'] == 'share': - data_list = request.POST.getlist('data') - data_list.sort(key=len) - for share_path in data_list: - relpath = os.path.normpath(share_path).lstrip('/') - if '..' in relpath.split(os.path.sep): - raise Exception('Permission denied') - abspath = os.path.abspath(os.path.join(share_root, relpath)) - if os.path.commonprefix([share_root, abspath]) != share_root: - raise Exception('Bad file path on share: ' + abspath) - source_paths.append(abspath) - target_paths.append(os.path.join(upload_dir, relpath)) - else: - data_list = request.FILES.getlist('data') - - if len(data_list) > settings.LOCAL_LOAD_MAX_FILES_COUNT: - raise Exception( - 'Too many files. Please use download via share') - common_size = 0 - for f in data_list: - common_size += f.size - if common_size > settings.LOCAL_LOAD_MAX_FILES_SIZE: - raise Exception('Too many size. Please use download via share') - - for data_file in data_list: - source_paths.append(data_file.name) - path = os.path.join(upload_dir, data_file.name) - target_paths.append(path) - with open(path, 'wb') as upload_file: - for chunk in data_file.chunks(): - upload_file.write(chunk) - - params['SOURCE_PATHS'] = source_paths - params['TARGET_PATHS'] = target_paths - - task.create(db_task.id, params) - - return JsonResponse({'tid': db_task.id}) - except Exception as exc: - slogger.glob.error("cannot create task {}".format( - params['task_name']), exc_info=True) - db_task.delete() - return HttpResponseBadRequest(str(exc)) - - return JsonResponse({'tid': db_task.id}) - - @login_required # @permission_required(perm=['engine.task.access'], # fn=objectgetter(models.Task, 'tid'), raise_exception=True)