From 0d5cecb927205d056d0f4734a4dc6f87ef8cfb79 Mon Sep 17 00:00:00 2001 From: Clemens Wolff Date: Fri, 6 Dec 2019 17:48:28 +1000 Subject: [PATCH 01/14] Add support for per-document annotation guideline --- app/server/static/components/annotationMixin.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/server/static/components/annotationMixin.js b/app/server/static/components/annotationMixin.js index b1c1d2dd..d194252f 100644 --- a/app/server/static/components/annotationMixin.js +++ b/app/server/static/components/annotationMixin.js @@ -273,7 +273,13 @@ export default { }, compiledMarkdown() { - return marked(this.guideline, { + const documentMetadata = this.documentMetadata; + + const guideline = documentMetadata && documentMetadata.guideline + ? documentMetadata.guideline + : this.guideline; + + return marked(guideline, { sanitize: true, }); }, From 331e12ff288ebb220ad3f3cf22923c5cdd29e217 Mon Sep 17 00:00:00 2001 From: Clemens Wolff Date: Fri, 6 Dec 2019 18:04:32 +1000 Subject: [PATCH 02/14] Hide special metadata values from display --- app/server/static/components/annotation.pug | 2 +- app/server/static/components/annotationMixin.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/server/static/components/annotation.pug b/app/server/static/components/annotation.pug index a3aaacf8..5aed7acb 100644 --- a/app/server/static/components/annotation.pug +++ b/app/server/static/components/annotation.pug @@ -103,7 +103,7 @@ div.columns(v-cloak="") ) section.modal-card-body.modal-card-body-footer vue-json-pretty( - v-bind:data="documentMetadata" + v-bind:data="displayDocumentMetadata" v-bind:show-double-quotes="false" v-bind:show-line="false" ) diff --git a/app/server/static/components/annotationMixin.js b/app/server/static/components/annotationMixin.js index d194252f..02d16a28 100644 --- a/app/server/static/components/annotationMixin.js +++ b/app/server/static/components/annotationMixin.js @@ -297,6 +297,18 @@ export default { : 'Click to approve annotations'; }, + displayDocumentMetadata() { + let documentMetadata = this.documentMetadata; + if (documentMetadata == null) { + return null; + } + + documentMetadata = { ...documentMetadata }; + delete documentMetadata.guideline; + delete documentMetadata.documentSourceUrl; + return documentMetadata; + }, + documentMetadata() { const document = this.docs[this.pageNumber]; if (document == null || document.meta == null) { From 18f4f650d806cf6ca37e94f96f1eb2dffea38cbe Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 1 Dec 2020 23:21:55 +0100 Subject: [PATCH 03/14] Feature: FastText Export (#362) --- app/api/tests/test_api.py | 5 ++++ app/api/utils.py | 40 ++++++++++++++++++++++++++- app/api/views.py | 14 ++++++---- frontend/services/document.service.js | 3 ++ frontend/store/projects.js | 12 +++++++- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/app/api/tests/test_api.py b/app/api/tests/test_api.py index 8a77179b..aa877c97 100644 --- a/app/api/tests/test_api.py +++ b/app/api/tests/test_api.py @@ -1528,6 +1528,11 @@ class TestDownloader(APITestCase): format='plain', expected_status=status.HTTP_400_BAD_REQUEST) + def test_can_download_classification_fasttext(self): + self.download_test_helper(url=self.classification_url, + format='txt', + expected_status=status.HTTP_200_OK) + class TestStatisticsAPI(APITestCase, TestUtilsMixin): diff --git a/app/api/utils.py b/app/api/utils.py index 977cba5a..7edda553 100644 --- a/app/api/utils.py +++ b/app/api/utils.py @@ -13,7 +13,7 @@ from django.db import transaction from django.conf import settings from colour import Color import pyexcel -from rest_framework.renderers import JSONRenderer +from rest_framework.renderers import JSONRenderer, BaseRenderer from seqeval.metrics.sequence_labeling import get_entities from .exceptions import FileParseException @@ -497,6 +497,44 @@ class JSONLRenderer(JSONRenderer): allow_nan=not self.strict) + '\n' +class FastTextPainter(object): + + @staticmethod + def paint_labels(documents, labels): + serializer = DocumentSerializer(documents, many=True) + serializer_labels = LabelSerializer(labels, many=True) + data = [] + for d in serializer.data: + labels = [] + for a in d['annotations']: + label_obj = [x for x in serializer_labels.data if x['id'] == a['label']][0] + labels.append('__label__{}'.format(label_obj['text'].replace(' ', '_'))) + text = d['text'].replace('\n', ' ') + if labels: + data.append('{} {}'.format(' '.join(labels), text)) + else: + data.append(text) + return data + + +class PlainTextRenderer(BaseRenderer): + media_type = 'text/plain' + format = 'txt' + charset = 'utf-8' + + def render(self, data, accepted_media_type=None, renderer_context=None): + if data is None: + return bytes() + + if not isinstance(data, list): + data = [data] + + buffer = io.BytesIO() + for d in data: + buffer.write((d + '\n').encode(self.charset)) + return buffer.getvalue() + + class JSONPainter(object): def paint(self, documents): diff --git a/app/api/views.py b/app/api/views.py index 20dd1ce2..1fff9770 100644 --- a/app/api/views.py +++ b/app/api/views.py @@ -24,9 +24,9 @@ from .models import Project, Label, Document, RoleMapping, Role from .permissions import IsProjectAdmin, IsAnnotatorAndReadOnly, IsAnnotator, IsAnnotationApproverAndReadOnly, IsOwnAnnotation, IsAnnotationApprover from .serializers import ProjectSerializer, LabelSerializer, DocumentSerializer, UserSerializer, ApproverSerializer from .serializers import ProjectPolymorphicSerializer, RoleMappingSerializer, RoleSerializer -from .utils import CSVParser, ExcelParser, JSONParser, PlainTextParser, CoNLLParser, AudioParser, FastTextParser, iterable_to_io -from .utils import JSONLRenderer -from .utils import JSONPainter, CSVPainter +from .utils import CSVParser, ExcelParser, JSONParser, PlainTextParser, FastTextParser, CoNLLParser, AudioParser, iterable_to_io +from .utils import JSONLRenderer, PlainTextRenderer +from .utils import JSONPainter, CSVPainter, FastTextPainter IsInProjectReadOnlyOrAdmin = (IsAnnotatorAndReadOnly | IsAnnotationApproverAndReadOnly | IsProjectAdmin) IsInProjectOrAdmin = (IsAnnotator | IsAnnotationApprover | IsProjectAdmin) @@ -359,7 +359,7 @@ class CloudUploadAPI(APIView): class TextDownloadAPI(APIView): permission_classes = TextUploadAPI.permission_classes - renderer_classes = (CSVRenderer, JSONLRenderer) + renderer_classes = (CSVRenderer, JSONLRenderer, PlainTextRenderer) def get(self, request, *args, **kwargs): format = request.query_params.get('q') @@ -369,9 +369,9 @@ class TextDownloadAPI(APIView): # jsonl-textlabel format prints text labels while jsonl format prints annotations with label ids # jsonl-textlabel format - "labels": [[0, 15, "PERSON"], ..] # jsonl format - "annotations": [{"label": 5, "start_offset": 0, "end_offset": 2, "user": 1},..] - if format == 'jsonl': + if format in ('jsonl', 'txt'): labels = project.labels.all() - data = JSONPainter.paint_labels(documents, labels) + data = painter.paint_labels(documents, labels) else: data = painter.paint(documents) return Response(data) @@ -381,6 +381,8 @@ class TextDownloadAPI(APIView): return CSVPainter() elif format == 'jsonl' or format == 'json': return JSONPainter() + elif format == 'txt': + return FastTextPainter() else: raise ValidationError('format {} is invalid.'.format(format)) diff --git a/frontend/services/document.service.js b/frontend/services/document.service.js index 8f72d8f0..09a2a131 100644 --- a/frontend/services/document.service.js +++ b/frontend/services/document.service.js @@ -30,6 +30,9 @@ class DocumentService { if (format === 'csv') { headers.Accept = 'text/csv; charset=utf-8' headers['Content-Type'] = 'text/csv; charset=utf-8' + } else if (format === 'txt') { + headers.Accept = 'text/plain; charset=utf-8' + headers['Content-Type'] = 'text/plain; charset=utf-8' } else { headers.Accept = 'application/json' headers['Content-Type'] = 'application/json' diff --git a/frontend/store/projects.js b/frontend/store/projects.js index 5622be20..48d057c0 100644 --- a/frontend/store/projects.js +++ b/frontend/store/projects.js @@ -180,6 +180,11 @@ export const getters = { text: 'JSONL(Text label)', suffix: 'jsonl' } + const fastText = { + type: 'txt', + text: 'FastText', + suffix: 'txt' + } if (state.current.project_type === 'DocumentClassification') { json.examples = [ '{"id": 1, "text": "Terrible customer service.", "annotations": [{"id": 1, "label": 1, "user": 1}]}\n', @@ -192,9 +197,14 @@ export const getters = { '2,"Really great transaction.",2,1\n', '3,"Great price.",2,1' ] + fastText.examples = [ + '__label__pet dog cat \n', + '__label__car VW BMW' + ] return [ csv, - json + json, + fastText ] } else if (state.current.project_type === 'SequenceLabeling') { json.examples = [ From cfafe1fd663f2d5c4ac8bedbe00c321435d550a6 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 18 Jan 2021 20:41:45 +0100 Subject: [PATCH 04/14] Add feature bulkimport for same filetype --- .../documents/DocumentUploadForm.vue | 39 +++++++++++++------ frontend/i18n/en/projects/dataset.js | 2 +- frontend/i18n/en/projects/errors.js | 2 +- frontend/rules/index.js | 2 +- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/frontend/components/organisms/documents/DocumentUploadForm.vue b/frontend/components/organisms/documents/DocumentUploadForm.vue index 623ae1d3..6159728a 100644 --- a/frontend/components/organisms/documents/DocumentUploadForm.vue +++ b/frontend/components/organisms/documents/DocumentUploadForm.vue @@ -18,7 +18,7 @@ type="error" dismissible > - {{ $t('errors.fileCannotUpload') }} + {{ $t('errors.fileCannotUpload') + errorMsg }}

{{ $t('dataset.importDataMessage1') }}

{{ $t('dataset.importDataMessage2') }} { - this.reset() - this.cancel() + this.errors = [] + const promises = [] + const id = this.$route.params.id + const type = this.selectedFormat.type + this.file.forEach((item) => { + promises.push({ + projectId: id, + format: type, + file: item }) - .catch(() => { + }) + let p = Promise.resolve() + promises.forEach((item) => { + p = p.then(() => this.uploadDocument(item)).catch(() => { + this.errors.push(item.file.name) this.showError = true }) + }) + p.finally(() => { + if (!this.errors.length) { + this.reset() + this.cancel() + } else { + this.errorMsg = this.errors.join(', ') + } + }) } } } diff --git a/frontend/i18n/en/projects/dataset.js b/frontend/i18n/en/projects/dataset.js index ac8cccd1..d645083b 100644 --- a/frontend/i18n/en/projects/dataset.js +++ b/frontend/i18n/en/projects/dataset.js @@ -9,7 +9,7 @@ export default { annotate: 'Annotate', importDataTitle: 'Upload Data', importDataMessage1: 'Select a file format', - importDataMessage2: 'Select a file', + importDataMessage2: 'Select file(s)', importDataPlaceholder: 'File input', exportDataTitle: 'Export Data', exportDataMessage: 'Select a file format', diff --git a/frontend/i18n/en/projects/errors.js b/frontend/i18n/en/projects/errors.js index f84d04a9..fcb93846 100644 --- a/frontend/i18n/en/projects/errors.js +++ b/frontend/i18n/en/projects/errors.js @@ -1,5 +1,5 @@ export default { - fileCannotUpload: 'The file could not be uploaded. Maybe invalid format.\n Please check available formats carefully.', + fileCannotUpload: 'The file(s) could not be uploaded. Maybe invalid format.\n Please check available formats and the following file(s): ', labelCannotCreate: 'The label could not be created.\n You cannot use the same label name or shortcut key.', invalidUserOrPass: 'Incorrect username or password, or something went wrong.' } diff --git a/frontend/rules/index.js b/frontend/rules/index.js index 3f5219b3..51224440 100644 --- a/frontend/rules/index.js +++ b/frontend/rules/index.js @@ -57,7 +57,7 @@ export const fileFormatRules = (msg) => { export const uploadFileRules = (msg) => { return [ v => !!v || msg.fileRequired, - v => !v || v.size < 1000000 || msg.fileLessThan1MB + v => !v || v.some(file => file.size < 1000000) || msg.fileLessThan1MB ] } From 9b688b756dc2df0b0857b6d901705a946c988258 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 18 Jan 2021 23:43:40 +0100 Subject: [PATCH 05/14] Add german translation of the frontend --- frontend/i18n/de/generic.js | 20 +++++++++++++ frontend/i18n/de/header.js | 3 ++ frontend/i18n/de/home.js | 18 ++++++++++++ frontend/i18n/de/index.js | 35 +++++++++++++++++++++++ frontend/i18n/de/projects/annotation.js | 14 +++++++++ frontend/i18n/de/projects/dataset.js | 19 +++++++++++++ frontend/i18n/de/projects/errors.js | 5 ++++ frontend/i18n/de/projects/guideline.js | 4 +++ frontend/i18n/de/projects/home.js | 11 +++++++ frontend/i18n/de/projects/labels.js | 17 +++++++++++ frontend/i18n/de/projects/members.js | 16 +++++++++++ frontend/i18n/de/projects/overview.js | 19 +++++++++++++ frontend/i18n/de/projects/statistics.js | 9 ++++++ frontend/i18n/de/rules.js | 38 +++++++++++++++++++++++++ frontend/i18n/de/toastui.js | 3 ++ frontend/i18n/de/user.js | 6 ++++ frontend/i18n/de/vuetify.js | 4 +++ frontend/i18n/index.js | 6 ++++ 18 files changed, 247 insertions(+) create mode 100644 frontend/i18n/de/generic.js create mode 100644 frontend/i18n/de/header.js create mode 100644 frontend/i18n/de/home.js create mode 100644 frontend/i18n/de/index.js create mode 100644 frontend/i18n/de/projects/annotation.js create mode 100644 frontend/i18n/de/projects/dataset.js create mode 100644 frontend/i18n/de/projects/errors.js create mode 100644 frontend/i18n/de/projects/guideline.js create mode 100644 frontend/i18n/de/projects/home.js create mode 100644 frontend/i18n/de/projects/labels.js create mode 100644 frontend/i18n/de/projects/members.js create mode 100644 frontend/i18n/de/projects/overview.js create mode 100644 frontend/i18n/de/projects/statistics.js create mode 100644 frontend/i18n/de/rules.js create mode 100644 frontend/i18n/de/toastui.js create mode 100644 frontend/i18n/de/user.js create mode 100644 frontend/i18n/de/vuetify.js diff --git a/frontend/i18n/de/generic.js b/frontend/i18n/de/generic.js new file mode 100644 index 00000000..1399e952 --- /dev/null +++ b/frontend/i18n/de/generic.js @@ -0,0 +1,20 @@ +export default { + continue: 'Weiter', + yes: 'Ja', + all: 'Alle', + save: 'Speichern', + edit: 'Editieren', + create: 'Erstellen', + cancel: 'Abbrechen', + close: 'Schließen', + upload: 'Hochladen', + add: 'Hinzufügen', + delete: 'Löschen', + search: 'Suchen', + name: 'Name', + import: 'Importieren', + export: 'Exportieren', + description: 'Beschreibung', + type: 'Typ', + loading: 'Laden... bitte warten' +} diff --git a/frontend/i18n/de/header.js b/frontend/i18n/de/header.js new file mode 100644 index 00000000..dbffe347 --- /dev/null +++ b/frontend/i18n/de/header.js @@ -0,0 +1,3 @@ +export default { + projects: 'Projekte' +} diff --git a/frontend/i18n/de/home.js b/frontend/i18n/de/home.js new file mode 100644 index 00000000..2d512f9a --- /dev/null +++ b/frontend/i18n/de/home.js @@ -0,0 +1,18 @@ +export default { + mainTitle: 'Text-Annotation für Menschen', + getStarted: 'Leg los', + startAnnotation: 'Starte Annotation', + featuresTitle: 'Die besten Funktionen', + featuresTitle1: 'Zusammenarbeit im Team', + featuresText1: 'Annotiere mit Teamkollegen', + featuresTitle2: 'Jede Sprache', + featuresText2: 'Annotiere mit jeder Sprache', + featuresTitle3: 'Open Source', + featuresText3: 'Kostenlos und anpassbar', + footerTitle: 'Schnelles Realisieren von Ideen', + demoDropDown: 'Demo ausprobieren', + demoNER: 'Named Entity Recognition', + demoSent: 'Sentiment Analysis', + demoTranslation: 'Übersetzung', + demoTextToSQL: 'Text zu SQL' +} diff --git a/frontend/i18n/de/index.js b/frontend/i18n/de/index.js new file mode 100644 index 00000000..0f1ae8e6 --- /dev/null +++ b/frontend/i18n/de/index.js @@ -0,0 +1,35 @@ +import home from './home' +import header from './header' +import generic from './generic' +import rules from './rules' +import toastui from './toastui' +import user from './user' +import vuetify from './vuetify' +import annotation from './projects/annotation' +import dataset from './projects/dataset' +import errors from './projects/errors' +import guideline from './projects/guideline' +import projectHome from './projects/home' +import labels from './projects/labels' +import members from './projects/members' +import overview from './projects/overview' +import statistics from './projects/statistics' + +export default { + home, + header, + generic, + rules, + toastui, + user, + vuetify, + annotation, + dataset, + errors, + guideline, + projectHome, + labels, + members, + overview, + statistics +} diff --git a/frontend/i18n/de/projects/annotation.js b/frontend/i18n/de/projects/annotation.js new file mode 100644 index 00000000..76b9a064 --- /dev/null +++ b/frontend/i18n/de/projects/annotation.js @@ -0,0 +1,14 @@ +export default { + checkedTooltip: 'Geprüft', + notCheckedTooltip: 'Nicht geprüft', + selectFilterTooltip: 'Wähle einen Filter', + filterOption1: 'Alle', + filterOption2: 'Erledigt', + filterOption3: 'Unerledigt', + guidelineTooltip: 'Zeige Leitfaden', + guidelinePopupTitle: 'Annotationsleitfaden', + metadataDefaultMessage: 'Keine Daten verfügbar', + key: 'Schlüssel', + value: 'Wert', + newText: 'Neuer Text' +} diff --git a/frontend/i18n/de/projects/dataset.js b/frontend/i18n/de/projects/dataset.js new file mode 100644 index 00000000..edec0272 --- /dev/null +++ b/frontend/i18n/de/projects/dataset.js @@ -0,0 +1,19 @@ +export default { + dataset: 'Datensatz', + actions: 'Aktionen', + importDataset: 'Importiere Datensatz', + exportDataset: 'Exportiere Datensatz', + text: 'Text', + metadata: 'Metadaten', + action: 'Aktion', + annotate: 'Annotiere', + importDataTitle: 'Daten hochladen', + importDataMessage1: 'Wähle ein Dateiformat', + importDataMessage2: 'Wähle Datei(en)', + importDataPlaceholder: 'Dateieingabe', + exportDataTitle: 'Exportiere Daten', + exportDataMessage: 'Wähle ein Dateiformat', + deleteDocumentsTitle: 'Dokument löschen', + deleteDocumentsMessage: 'Bist du dir sicher, dass du die Dokumente aus dem Projekt löschen willst?', + pageText: '{0}-{1} von {2}' +} diff --git a/frontend/i18n/de/projects/errors.js b/frontend/i18n/de/projects/errors.js new file mode 100644 index 00000000..c9ead2c6 --- /dev/null +++ b/frontend/i18n/de/projects/errors.js @@ -0,0 +1,5 @@ +export default { + fileCannotUpload: 'Die Datei(en) konnten nicht hochgeladen werden. Vielleicht ungültiges Format.\n Bitte prüfe die verfügbaren Dateiformate und folgende Datei(en): ', + labelCannotCreate: 'Das Label konnte nicht erstellt werden.\n Jeder Labelname und jedes Tastenkürzel kann nur einmal vergeben werden.', + invalidUserOrPass: 'Falscher Benutername oder falsches Passwort, oder etwas ist schief gelaufen.' +} diff --git a/frontend/i18n/de/projects/guideline.js b/frontend/i18n/de/projects/guideline.js new file mode 100644 index 00000000..3e42552a --- /dev/null +++ b/frontend/i18n/de/projects/guideline.js @@ -0,0 +1,4 @@ +export default { + guideline: 'Leitfaden', + writeGuidelinePrompt: 'Bitte schreib einen Annotationsleitfaden.' +} diff --git a/frontend/i18n/de/projects/home.js b/frontend/i18n/de/projects/home.js new file mode 100644 index 00000000..03298773 --- /dev/null +++ b/frontend/i18n/de/projects/home.js @@ -0,0 +1,11 @@ +export default { + home: 'Startseite', + welcome: 'Willkommen bei Doccano!', + importData: 'Importiere einen Datensatz', + createLabels: 'Erstelle Labels für dieses Projekt', + addMembers: 'Mitglieder für kollaboratives Arbeiten hinzufügen', + defineGuideline: 'Definiere einen Leitfaden für das Projekt', + annotateDataset: 'Annotiere den Datensatz', + viewStatistics: 'Statistiken anzeigen', + exportDataset: 'Exportiere den Datensatz' +} diff --git a/frontend/i18n/de/projects/labels.js b/frontend/i18n/de/projects/labels.js new file mode 100644 index 00000000..058f7a7d --- /dev/null +++ b/frontend/i18n/de/projects/labels.js @@ -0,0 +1,17 @@ +export default { + labels: 'Labels', + shortkey: 'Tastenkürzel', + color: 'Farbe', + createLabel: 'Erstelle Label', + importLabels: 'Importiere Labels', + exportLabels: 'Exportiere Labels', + labelName: 'Labelname', + labelMessage: 'Labelname wird benötigt', + key: 'Schlüssel', + deleteLabel: 'Lösche Label', + deleteMessage: 'Bist du dir sicher, dass du diese Labels aus dem Projekt löschen willst?', + importTitle: 'Label hochladen', + importMessage1: 'Beispielformat', + importMessage2: 'Wähle eine Datei', + filePlaceholder: 'Eingabe einer Datei' +} diff --git a/frontend/i18n/de/projects/members.js b/frontend/i18n/de/projects/members.js new file mode 100644 index 00000000..0d19d2d2 --- /dev/null +++ b/frontend/i18n/de/projects/members.js @@ -0,0 +1,16 @@ +export default { + members: 'Mitglieder', + role: 'Rolle', + updateRole: 'Rolle aktualisieren', + addMember: 'Mitglied hinzufügen', + userSearchAPIs: 'Nutzer-Such-APIs', + userSearchPrompt: 'Tippe, um zu suchen', + removeMember: 'Mitglied entfernen', + removePrompt: 'Bist du dir sicher, dass du diese Mitglieder entfernen willst?', + roles: { + projectAdmin: 'Projektadministrator', + annotator: 'Annotator', + annotationApprover: 'Annotationsmoderator', + undefined: 'Keine Rolle definiert' + } +} diff --git a/frontend/i18n/de/projects/overview.js b/frontend/i18n/de/projects/overview.js new file mode 100644 index 00000000..5df6bdd0 --- /dev/null +++ b/frontend/i18n/de/projects/overview.js @@ -0,0 +1,19 @@ +export default { + createProjectTitle: 'Projekt hinzufügen', + projectName: 'Projektname', + projectType: 'Projekttyp', + textClassification: 'Textklassifikation', + sequenceLabeling: 'Sequenz-Labeling', + sequenceToSequence: 'Sequenz zu Sequenz', + randomizeDocOrder: 'Dokumentenreihenfolge zufällig wählen', + shareAnnotations: 'Gemeinsame Annotationen für Nutzer', + delete: 'Löschen', + deleteProjectTitle: 'Lösche Projekt', + deleteProjectMessage: 'Bist du dir sicher, dass du diese Projekte löschen willst?', + search: 'Suchen', + projectTypes: [ + 'Textklassifikation', + 'Sequenz-Labeling', + 'Sequenz zu Sequenz' + ] +} diff --git a/frontend/i18n/de/projects/statistics.js b/frontend/i18n/de/projects/statistics.js new file mode 100644 index 00000000..57053ca1 --- /dev/null +++ b/frontend/i18n/de/projects/statistics.js @@ -0,0 +1,9 @@ +export default { + statistics: 'Statistiken', + progress: [ + 'Abgeschlossen', + 'Unvollständig' + ], + labelStats: 'Labelstatistiken', + userStats: 'Nutzerstatistiken' +} diff --git a/frontend/i18n/de/rules.js b/frontend/i18n/de/rules.js new file mode 100644 index 00000000..a60b6c1b --- /dev/null +++ b/frontend/i18n/de/rules.js @@ -0,0 +1,38 @@ +export default { + colorRules: { + colorRequired: 'Farbe wird benötigt' + }, + labelNameRules: { + labelRequired: 'Labelname wird benötigt', + labelLessThan30Chars: 'Labelname muss kürzer als 30 Zeichen sein' + }, + userNameRules: { + userNameRequired: 'Benutzername wird benötigt', + userNameLessThan30Chars: 'Benutzername muss kürzer als 30 Zeichen sein' + }, + roleRules: { + roleRequired: 'Rolle wird benötigt' + }, + projectNameRules: { + projectNameRequired: 'Projektname wird benötigt', + projectNameLessThan30Chars: 'Projektname muss kürzer als 30 Zeichen sein' + }, + descriptionRules: { + descriptionRequired: 'Beschreibung wird benötigt', + descriptionLessThan30Chars: 'Beschreibung muss kürzer als 100 Zeichen sein' + }, + projectTypeRules: { + projectTypeRequired: 'Projekttyp wird benötigt' + }, + fileFormatRules: { + fileFormatRequired: 'Dateiformat wird benötigt' + }, + uploadFileRules: { + fileRequired: 'Datei(en) werden benötigt', + fileLessThan1MB: 'Dateigröße muss kleiner als 1 MB sein!' + }, + passwordRules: { + passwordRequired: 'Passwort wird benötigt', + passwordLessThan30Chars: 'Passwort muss kürzer als 30 Zeichen sein' + } +} diff --git a/frontend/i18n/de/toastui.js b/frontend/i18n/de/toastui.js new file mode 100644 index 00000000..7d25f5b7 --- /dev/null +++ b/frontend/i18n/de/toastui.js @@ -0,0 +1,3 @@ +export default { + localeCode: 'de_DE' +} diff --git a/frontend/i18n/de/user.js b/frontend/i18n/de/user.js new file mode 100644 index 00000000..93da3aaf --- /dev/null +++ b/frontend/i18n/de/user.js @@ -0,0 +1,6 @@ +export default { + login: 'Einloggen', + signOut: 'Ausloggen', + username: 'Benutzername', + password: 'Passwort' +} diff --git a/frontend/i18n/de/vuetify.js b/frontend/i18n/de/vuetify.js new file mode 100644 index 00000000..1dc1e0a5 --- /dev/null +++ b/frontend/i18n/de/vuetify.js @@ -0,0 +1,4 @@ +export default { + itemsPerPageText: 'Einträge pro Seite', + noDataAvailable: 'Keine Daten verfügbar' +} diff --git a/frontend/i18n/index.js b/frontend/i18n/index.js index be51924a..83b3145d 100644 --- a/frontend/i18n/index.js +++ b/frontend/i18n/index.js @@ -17,6 +17,12 @@ export default { code: 'fr', iso: 'fr-CA', file: 'fr' + }, + { + name: 'Deutsch', + code: 'de', + iso: 'de-DE', + file: 'de' } ], lazy: true, From b6b6ca5a3d155d93936f5d139e2f078ef820589d Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 21 Jan 2021 02:06:51 +0100 Subject: [PATCH 06/14] fix chinese language iso code --- frontend/i18n/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/i18n/index.js b/frontend/i18n/index.js index 83b3145d..f36dd3fb 100644 --- a/frontend/i18n/index.js +++ b/frontend/i18n/index.js @@ -9,7 +9,7 @@ export default { { name: '中文', code: 'zh', - iso: 'zh-CA', + iso: 'zh-CN', file: 'zh' }, { From 9bb80235ee17ea744281c3b1c5bb454e8601657d Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 23 Jan 2021 00:50:16 +0100 Subject: [PATCH 07/14] Added API-Test Doc to README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f9ffeddb..ac5c73e9 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,14 @@ $ docker-compose -f docker-compose.dev.yml up Go to . + +Run Backend API-Tests: + +You can run the API-Tests for the backend with the following command: +```bash +docker exec doccano_backend_1 pipenv run app/manage.py test api +``` + ### Add annotators (optionally) If you want to add annotators/annotation approvers, see [Frequently Asked Questions](./docs/faq.md) From 675357d45eeca706fe2116ddd4c0da4552b69e30 Mon Sep 17 00:00:00 2001 From: Richard Hill Date: Sun, 24 Jan 2021 11:11:47 +0100 Subject: [PATCH 08/14] Increased max upload size to 100 MB. --- frontend/i18n/en/rules.js | 2 +- frontend/rules/index.js | 2 +- nginx/nginx.conf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/i18n/en/rules.js b/frontend/i18n/en/rules.js index e219d293..d9f54c23 100644 --- a/frontend/i18n/en/rules.js +++ b/frontend/i18n/en/rules.js @@ -29,7 +29,7 @@ export default { }, uploadFileRules: { fileRequired: 'File is required', - fileLessThan1MB: 'File size should be less than 1 MB!' + fileLessThan1MB: 'File size should be less than 100 MB!' }, passwordRules: { passwordRequired: 'Password is required', diff --git a/frontend/rules/index.js b/frontend/rules/index.js index 3f5219b3..c7fd3e8b 100644 --- a/frontend/rules/index.js +++ b/frontend/rules/index.js @@ -57,7 +57,7 @@ export const fileFormatRules = (msg) => { export const uploadFileRules = (msg) => { return [ v => !!v || msg.fileRequired, - v => !v || v.size < 1000000 || msg.fileLessThan1MB + v => !v || v.size < 100000000 || msg.fileLessThan1MB ] } diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 3d482f4c..d6d88ce3 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -1,7 +1,7 @@ server { listen 80; charset utf-8; - + client_max_body_size 1000M; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; From e39a4c2c4290d6d34ad4dbeb01821168be98c93a Mon Sep 17 00:00:00 2001 From: Hironsan Date: Mon, 25 Jan 2021 11:50:57 +0900 Subject: [PATCH 09/14] Fix client_max_body_size to 100M --- nginx/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index d6d88ce3..22ac7d7f 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -1,7 +1,7 @@ server { listen 80; charset utf-8; - client_max_body_size 1000M; + client_max_body_size 100M; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; From 49bf17110d2149c51790b11f3f634911ef10e89d Mon Sep 17 00:00:00 2001 From: Hironsan Date: Tue, 26 Jan 2021 14:35:26 +0900 Subject: [PATCH 10/14] Fix a method counts the number of characters --- .../organisms/annotation/EntityItemBox.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/components/organisms/annotation/EntityItemBox.vue b/frontend/components/organisms/annotation/EntityItemBox.vue index 3a65e9bb..901455b6 100644 --- a/frontend/components/organisms/annotation/EntityItemBox.vue +++ b/frontend/components/organisms/annotation/EntityItemBox.vue @@ -98,24 +98,27 @@ export default { chunks() { let chunks = [] - const entities = this.sortedEntities let startOffset = 0 - for (const entity of entities) { + // to count the number of characters correctly. + const characters = [...this.text] + for (const entity of this.sortedEntities) { // add non-entities to chunks. - chunks = chunks.concat(this.makeChunks(this.text.slice(startOffset, entity.start_offset))) + let piece = characters.slice(startOffset, entity.start_offset).join('') + chunks = chunks.concat(this.makeChunks(piece)) startOffset = entity.end_offset // add entities to chunks. const label = this.labelObject[entity.label] + piece = characters.slice(entity.start_offset, entity.end_offset).join('') chunks.push({ id: entity.id, label: label.text, color: label.background_color, - text: this.text.slice(entity.start_offset, entity.end_offset) + text: piece }) } // add the rest of text. - chunks = chunks.concat(this.makeChunks(this.text.slice(startOffset, this.text.length))) + chunks = chunks.concat(this.makeChunks(characters.slice(startOffset, characters.length).join(''))) return chunks }, From 5d0568bcf2b804bb9ab2ecd4210c56fe7634f42e Mon Sep 17 00:00:00 2001 From: Hironsan Date: Wed, 27 Jan 2021 09:48:34 +0900 Subject: [PATCH 11/14] Update pypi publish workflow --- .github/workflows/pypi-publish.yml | 6 +++--- Pipfile | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 8fbdc214..c35beb86 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -32,11 +32,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel - pip install -r requirements.txt + pip install pipenv + pipenv sync --dev - name: collectstatic run: | - python manage.py collectstatic --noinput + pipenv run collectstatic working-directory: ./app - name: Build a binary wheel and a source tarball run: | diff --git a/Pipfile b/Pipfile index 9e887c66..bacbd998 100644 --- a/Pipfile +++ b/Pipfile @@ -52,3 +52,4 @@ isort = "isort . -c" wait_for_db = "python manage.py wait_for_db" test = "python manage.py test api.tests" migrate = "python manage.py migrate" +collectstatic = "python manage.py collectstatic --noinput" From 507f11e0717d9d378a526dcf53b352c4d0191a25 Mon Sep 17 00:00:00 2001 From: Hironsan Date: Wed, 27 Jan 2021 11:13:12 +0900 Subject: [PATCH 12/14] Update pypi publish workflow --- .github/workflows/pypi-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index c35beb86..8c3c5d1c 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -40,7 +40,7 @@ jobs: working-directory: ./app - name: Build a binary wheel and a source tarball run: | - python setup.py sdist bdist_wheel + python setup.py sdist - name: Publish a Python distribution to PyPI uses: pypa/gh-action-pypi-publish@master with: From 646c68e8b110c6cd4b8ec7f5a11b7a3bdaf319b5 Mon Sep 17 00:00:00 2001 From: wujianchang Date: Thu, 28 Jan 2021 16:34:44 +0800 Subject: [PATCH 13/14] fix doc bug --- docs/project_structure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/project_structure.md b/docs/project_structure.md index c7615f97..4690e196 100644 --- a/docs/project_structure.md +++ b/docs/project_structure.md @@ -16,11 +16,11 @@ Consider them: **[app/](https://github.com/doccano/doccano/tree/master/app)** -The `app/api` directory contains backend code. See [below](#Backend). +The `app/` directory contains backend code. See [below](#Backend). **[frontend/](https://github.com/doccano/doccano/tree/master/frontend)** -The `app/api` directory contains frontend code. See [below](#Frontend). +The `frontend/` directory contains frontend code. See [below](#Frontend). **[docker-compose.dev.yml](https://github.com/doccano/doccano/blob/master/docker-compose.dev.yml)** From 786eb2e9a57cd9e48052dee10ba9e7361738772f Mon Sep 17 00:00:00 2001 From: Hironsan Date: Thu, 28 Jan 2021 16:56:26 +0900 Subject: [PATCH 14/14] Add project edit page --- .../organisms/layout/TheSideBar.vue | 3 +- frontend/i18n/en/index.js | 4 +- frontend/i18n/en/projects/settings.js | 3 + .../pages/projects/_id/settings/index.vue | 225 ++++++++++++++++++ 4 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 frontend/i18n/en/projects/settings.js create mode 100644 frontend/pages/projects/_id/settings/index.vue diff --git a/frontend/components/organisms/layout/TheSideBar.vue b/frontend/components/organisms/layout/TheSideBar.vue index 9992d080..0b33b48d 100644 --- a/frontend/components/organisms/layout/TheSideBar.vue +++ b/frontend/components/organisms/layout/TheSideBar.vue @@ -66,7 +66,8 @@ export default { { icon: 'label', text: this.$t('labels.labels'), link: 'labels', adminOnly: true }, { icon: 'person', text: this.$t('members.members'), link: 'members', adminOnly: true }, { icon: 'mdi-book-open-outline', text: this.$t('guideline.guideline'), link: 'guideline', adminOnly: true }, - { icon: 'mdi-chart-bar', text: this.$t('statistics.statistics'), link: 'statistics', adminOnly: true } + { icon: 'mdi-chart-bar', text: this.$t('statistics.statistics'), link: 'statistics', adminOnly: true }, + { icon: 'mdi-cog', text: this.$t('settings.title'), link: 'settings', adminOnly: true } ] return items.filter(item => this.isVisible(item)) } diff --git a/frontend/i18n/en/index.js b/frontend/i18n/en/index.js index 0f1ae8e6..0cb5adb3 100644 --- a/frontend/i18n/en/index.js +++ b/frontend/i18n/en/index.js @@ -14,6 +14,7 @@ import labels from './projects/labels' import members from './projects/members' import overview from './projects/overview' import statistics from './projects/statistics' +import settings from './projects/settings' export default { home, @@ -31,5 +32,6 @@ export default { labels, members, overview, - statistics + statistics, + settings } diff --git a/frontend/i18n/en/projects/settings.js b/frontend/i18n/en/projects/settings.js new file mode 100644 index 00000000..966e6918 --- /dev/null +++ b/frontend/i18n/en/projects/settings.js @@ -0,0 +1,3 @@ +export default { + title: 'Settings' +} diff --git a/frontend/pages/projects/_id/settings/index.vue b/frontend/pages/projects/_id/settings/index.vue new file mode 100644 index 00000000..e2f7974b --- /dev/null +++ b/frontend/pages/projects/_id/settings/index.vue @@ -0,0 +1,225 @@ + + +