diff --git a/doccano/app/app/settings.py b/doccano/app/app/settings.py index 0f93c003..a9fbcfd7 100644 --- a/doccano/app/app/settings.py +++ b/doccano/app/app/settings.py @@ -115,7 +115,8 @@ REST_FRAMEWORK = { ], 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 2, - 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',), + 'SEARCH_PARAM': 'q', } # Internationalization diff --git a/doccano/app/db.sqlite3 b/doccano/app/db.sqlite3 index 29a5856c..fb73d843 100644 Binary files a/doccano/app/db.sqlite3 and b/doccano/app/db.sqlite3 differ diff --git a/doccano/app/server/models.py b/doccano/app/server/models.py index a42cfe82..63424cbe 100644 --- a/doccano/app/server/models.py +++ b/doccano/app/server/models.py @@ -16,7 +16,7 @@ class Project(models.Model): class Label(models.Model): text = models.CharField(max_length=100, unique=True) shortcut = models.CharField(max_length=10, unique=True) - project = models.ForeignKey(Project, on_delete=models.CASCADE) + project = models.ForeignKey(Project, related_name='labels', on_delete=models.CASCADE) def as_dict(self): return {'id': self.id, @@ -29,7 +29,7 @@ class Label(models.Model): class Document(models.Model): text = models.TextField() - project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True) + project = models.ForeignKey(Project, related_name='documents', on_delete=models.CASCADE) def as_dict(self): return {'id': self.id, @@ -42,7 +42,7 @@ class Document(models.Model): class Annotation(models.Model): prob = models.FloatField(blank=True, null=True) label = models.ForeignKey(Label, on_delete=models.CASCADE) - data = models.ForeignKey(Document, related_name='annotations', on_delete=models.CASCADE) + data = models.ForeignKey(Document, related_name='labels', on_delete=models.CASCADE) manual = models.BooleanField(default=False) def as_dict(self): diff --git a/doccano/app/server/serializers.py b/doccano/app/server/serializers.py index 49fe01d3..f4a13557 100644 --- a/doccano/app/server/serializers.py +++ b/doccano/app/server/serializers.py @@ -3,13 +3,6 @@ from rest_framework import serializers from .models import Label, Project, Document, Annotation -class ProjectSerializer(serializers.ModelSerializer): - - class Meta: - model = Project - fields = ('id', 'name', 'description', 'created_at', 'updated_at', 'users') - - class LabelSerializer(serializers.ModelSerializer): class Meta: @@ -26,9 +19,17 @@ class AnnotationSerializer(serializers.ModelSerializer): class DocumentSerializer(serializers.ModelSerializer): - project = ProjectSerializer() - annotations = AnnotationSerializer(many=True) + labels = AnnotationSerializer(many=True) class Meta: model = Document - fields = ('id', 'text', 'project', 'annotations') + fields = ('id', 'text', 'labels') + + +class ProjectSerializer(serializers.ModelSerializer): + labels = LabelSerializer(many=True) + documents = DocumentSerializer(many=True) + + class Meta: + model = Project + fields = ('id', 'name', 'description', 'users', 'labels', 'documents') diff --git a/doccano/app/server/static/annotation.js b/doccano/app/server/static/annotation.js index 7e98bf94..70bb0c07 100644 --- a/doccano/app/server/static/annotation.js +++ b/doccano/app/server/static/annotation.js @@ -164,9 +164,9 @@ var vm = new Vue({ computed: { achievement: function () { if (this.total == 0) { - return 0; + return 0 } else { - return (this.total - this.remaining) / this.total * 100 + return Math.round((this.total - this.remaining) / this.total * 100) } }, progressColor: function () { diff --git a/doccano/app/server/views.py b/doccano/app/server/views.py index 9de4d352..5c3860f6 100644 --- a/doccano/app/server/views.py +++ b/doccano/app/server/views.py @@ -1,6 +1,6 @@ import json -import django_filters +from django_filters.rest_framework import DjangoFilterBackend from django.http import JsonResponse, HttpResponse from django.shortcuts import render from django.views import View @@ -8,6 +8,9 @@ from django.views.generic.list import ListView from django.views.generic.detail import DetailView from django.core.paginator import Paginator from rest_framework import viewsets, filters +from rest_framework.decorators import action +from rest_framework.response import Response + from .models import Annotation, Label, Document, Project from .serializers import LabelSerializer, ProjectSerializer, DocumentSerializer @@ -200,17 +203,53 @@ class DataDownloadAPI(View): return response -class LabelViewSet(viewsets.ModelViewSet): - queryset = Label.objects.all() - serializer_class = LabelSerializer - filter_fields = ('text', 'project') - - class ProjectViewSet(viewsets.ModelViewSet): queryset = Project.objects.all() serializer_class = ProjectSerializer + @action(methods=['get'], detail=True) + def labels(self, request, pk=None): + project = self.get_object() + res = {label.id: label.text for label in project.labels.all()} + return Response(res) + + @action(methods=['get'], detail=True) + def docs(self, request, pk=None): + project = self.get_object() + res = [doc.as_dict() for doc in project.documents.all()] + return Response(res) + + @action(methods=['get'], detail=True) + def progress(self, request, pk=None): + project = self.get_object() + docs = project.documents.all() + remaining = docs.filter(labels__isnull=True).count() + return Response({'total': docs.count(), 'remaining': remaining}) + + @action(methods=['post'], detail=True) + def upload_doc(self, request, pk=None): + project = self.get_object() + f = request.FILES['file'] + content = ''.join(chunk.decode('utf-8') for chunk in f.chunks()) + for line in content.split('\n'): + j = json.loads(line) + Document(project=project, text=j['text']).save() + + @action(methods=['get'], detail=True) + def download_doc(self, request, pk=None): + project = self.get_object() + res = [] + return res + class DocumentViewSet(viewsets.ModelViewSet): queryset = Document.objects.all() serializer_class = DocumentSerializer + filter_backends = (DjangoFilterBackend, filters.SearchFilter) + search_fields = ('text', ) + + +class LabelViewSet(viewsets.ModelViewSet): + queryset = Label.objects.all() + serializer_class = LabelSerializer + filter_fields = ('project',)