From 7b1bbc1aeae5f299856a856603562c1b85a73176 Mon Sep 17 00:00:00 2001 From: Arman Rahman Date: Wed, 17 Jul 2019 09:00:39 -0400 Subject: [PATCH] Implement data import from Excel --- .gitignore | 2 + README.md | 42 +++++++++------- app/api/tests/data/example.invalid.1.xlsx | Bin 0 -> 9837 bytes app/api/tests/data/example.invalid.2.xlsx | Bin 0 -> 9884 bytes app/api/tests/data/example.xlsx | Bin 0 -> 9844 bytes app/api/tests/data/example_one_column.csv | 4 ++ app/api/tests/data/example_one_column.xlsx | Bin 0 -> 9806 bytes .../data/example_one_column_no_header.xlsx | Bin 0 -> 9937 bytes app/api/tests/test_api.py | 45 +++++++++++++++++- app/api/utils.py | 25 +++++++++- app/api/views.py | 4 +- .../components/examples/upload_seq2seq.xlsx | Bin 0 -> 10891 bytes .../examples/upload_text_classification.xlsx | Bin 0 -> 10044 bytes .../static/components/upload_seq2seq.vue | 10 ++++ .../components/upload_text_classification.vue | 10 ++++ requirements.txt | 2 + 16 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 app/api/tests/data/example.invalid.1.xlsx create mode 100644 app/api/tests/data/example.invalid.2.xlsx create mode 100644 app/api/tests/data/example.xlsx create mode 100644 app/api/tests/data/example_one_column.csv create mode 100644 app/api/tests/data/example_one_column.xlsx create mode 100644 app/api/tests/data/example_one_column_no_header.xlsx create mode 100644 app/server/static/components/examples/upload_seq2seq.xlsx create mode 100644 app/server/static/components/examples/upload_text_classification.xlsx diff --git a/.gitignore b/.gitignore index 9320c27a..205f4f1c 100644 --- a/.gitignore +++ b/.gitignore @@ -198,3 +198,5 @@ pip-selfcheck.json node_modules/ bundle/ webpack-stats.json + +.vscode diff --git a/README.md b/README.md index fa304bb8..bef3469e 100644 --- a/README.md +++ b/README.md @@ -58,20 +58,19 @@ Doccano can be deployed to AWS ([Cloudformation](https://docs.aws.amazon.com/AWS > Notice: (1) EC2 KeyPair cannot be created automatically, so make sure you have an existing EC2 KeyPair in one region. Or [create one yourself](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair). (2) If you want to access doccano via HTTPS in AWS, here is an [instruction](https://github.com/chakki-works/doccano/wiki/HTTPS-setting-for-doccano-in-AWS). - ## Features -* Collaborative annotation -* Multi-Language support -* Emoji :smile: support -* (future) Auto labeling +- Collaborative annotation +- Multi-Language support +- Emoji :smile: support +- (future) Auto labeling ## Requirements -* Python 3.6+ -* Django 2.1.7+ -* Node.js 8.0+ -* Google Chrome(highly recommended) +- Python 3.6+ +- Django 2.1.7+ +- Node.js 8.0+ +- Google Chrome(highly recommended) ## Installation @@ -164,7 +163,9 @@ Finally, to start the server, run the following command: ```bash python manage.py runserver ``` + Optionally, you can change the bind ip and port using the command + ```bash python manage.py runserver : ``` @@ -199,20 +200,26 @@ After creating a project, you will see the "Import Data" page, or click `Import Upload project -You can upload two types of files: -- `CSV file`: file must contain a header with a `text` column or be one-column csv file. -- `JSON file`: each line contains a JSON object with a `text` key. JSON format supports line breaks rendering. +You can upload the following types of files (depending on project type): + +- `Text file`: file must contain one sentence/document per line separated by new lines. +- `CSV file`: file must contain a header with `"text"` as the first column or be one-column csv file. If using labels the sencond column must be the labels. +- `Excel file`: file must contain a header with `"text"` as the first column or be one-column excel file. If using labels the sencond column must be the labels. Supports multiple sheets as long as format is the same. +- `JSON file`: each line contains a JSON object with a `text` key. JSON format supports line breaks rendering. > Notice: Doccano won't render line breaks in annotation page for sequence labeling task due to the indent problem, but the exported JSON file still contains line breaks. -`example.txt` (or `example.csv`) -```python +`example.txt/csv/xlsx` + +```txt EU rejects German call to boycott British lamb. President Obama is speaking at the White House. He lives in Newark, Ohio. ... ``` + `example.json` + ```JSON {"text": "EU rejects German call to boycott British lamb."} {"text": "President Obama is speaking at the White House."} @@ -220,7 +227,7 @@ He lives in Newark, Ohio. ... ``` -Any other columns (for csv) or keys (for json) are preserved and will be exported in the `metadata` column or key as is. +Any other columns (for csv/excel) or keys (for json) are preserved and will be exported in the `metadata` column or key as is. Once you select a TXT/JSON file on your computer, click `Upload dataset` button. After uploading the dataset file, we will see the `Dataset` page (or click `Dataset` button list in the left bar). This page displays all the documents we uploaded in one project. @@ -230,7 +237,6 @@ Click `Labels` button in left bar to define your own labels. You should see the Edit label - ### Annotation Now, you are ready to annotate the texts. Just click the `Annotate Data` button in the navigation bar, you can start to annotate the documents you uploaded. @@ -251,11 +257,14 @@ by adding `external_id` to the imported file. For example: Input file may look like this: `import.json` + ```JSON {"text": "EU rejects German call to boycott British lamb.", "external_id": 1} ``` + and the exported file will look like this: `output.json` + ```JSON {"doc_id": 2023, "text": "EU rejects German call to boycott British lamb.", "labels": ["news"], "username": "root", "metadata": {"external_id": 1}} ``` @@ -272,7 +281,6 @@ As with any software, doccano is under continuous development. If you have reque Here are some tips might be helpful. [How to Contribute to Doccano Project](https://github.com/chakki-works/doccano/wiki/How-to-Contribute-to-Doccano-Project) - ## Contact For help and feedback, please feel free to contact [the author](https://github.com/Hironsan). diff --git a/app/api/tests/data/example.invalid.1.xlsx b/app/api/tests/data/example.invalid.1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0731da01b9690b1038629ece920032aa22a6ea0f GIT binary patch literal 9837 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 literal 0 HcmV?d00001 diff --git a/app/api/tests/data/example.invalid.2.xlsx b/app/api/tests/data/example.invalid.2.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..93205319ccb378609094c440f352a69476d8516c GIT binary patch literal 9884 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%0D~y};#Bl$Lzn)CJ6nf&h!=heL%F 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 OKcR5f-9-QM*Z%<{s3jW! literal 0 HcmV?d00001 diff --git a/app/api/tests/data/example_one_column_no_header.xlsx b/app/api/tests/data/example_one_column_no_header.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2bacb0327a6a9fa839a492c33a18df0521f7708f GIT binary patch literal 9937 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 literal 0 HcmV?d00001 diff --git a/app/api/tests/test_api.py b/app/api/tests/test_api.py index 25721aa1..4ca18e22 100644 --- a/app/api/tests/test_api.py +++ b/app/api/tests/test_api.py @@ -759,7 +759,7 @@ class TestUploader(APITestCase): def upload_test_helper(self, project_id, filename, file_format, expected_status, **kwargs): url = reverse(viewname='doc_uploader', args=[project_id]) - with open(os.path.join(DATA_DIR, filename)) as f: + with open(os.path.join(DATA_DIR, filename), 'rb') as f: response = self.client.post(url, data={'file': f, 'format': file_format}) self.assertEqual(response.status_code, expected_status) @@ -803,6 +803,12 @@ class TestUploader(APITestCase): file_format='csv', expected_status=status.HTTP_201_CREATED) + def test_can_upload_single_column_csv(self): + self.upload_test_helper(project_id=self.seq2seq_project.id, + filename='example_one_column.csv', + file_format='csv', + expected_status=status.HTTP_201_CREATED) + def test_cannot_upload_csv_file_does_not_match_column_and_row(self): self.upload_test_helper(project_id=self.classification_project.id, filename='example.invalid.1.csv', @@ -815,6 +821,43 @@ class TestUploader(APITestCase): file_format='csv', expected_status=status.HTTP_400_BAD_REQUEST) + def test_can_upload_classification_excel(self): + self.upload_test_helper(project_id=self.classification_project.id, + filename='example.xlsx', + file_format='excel', + expected_status=status.HTTP_201_CREATED) + + def test_can_upload_seq2seq_excel(self): + self.upload_test_helper(project_id=self.seq2seq_project.id, + filename='example.xlsx', + file_format='excel', + expected_status=status.HTTP_201_CREATED) + + def test_can_upload_single_column_excel(self): + self.upload_test_helper(project_id=self.seq2seq_project.id, + filename='example_one_column.xlsx', + file_format='excel', + expected_status=status.HTTP_201_CREATED) + + def test_cannot_upload_excel_file_does_not_match_column_and_row(self): + self.upload_test_helper(project_id=self.classification_project.id, + filename='example.invalid.1.xlsx', + file_format='excel', + expected_status=status.HTTP_400_BAD_REQUEST) + + def test_cannot_upload_excel_file_has_too_many_columns(self): + self.upload_test_helper(project_id=self.classification_project.id, + filename='example.invalid.2.xlsx', + file_format='excel', + expected_status=status.HTTP_400_BAD_REQUEST) + + @override_settings(IMPORT_BATCH_SIZE=1) + def test_can_upload_small_batch_size(self): + self.upload_test_helper(project_id=self.seq2seq_project.id, + filename='example_one_column_no_header.xlsx', + file_format='excel', + expected_status=status.HTTP_201_CREATED) + def test_can_upload_classification_jsonl(self): self.upload_test_helper(project_id=self.classification_project.id, filename='classification.jsonl', diff --git a/app/api/utils.py b/app/api/utils.py index 2f9fca92..77ae4758 100644 --- a/app/api/utils.py +++ b/app/api/utils.py @@ -8,6 +8,7 @@ from random import Random from django.db import transaction from django.conf import settings +import pyexcel from rest_framework.renderers import JSONRenderer from seqeval.metrics.sequence_labeling import get_entities @@ -318,13 +319,32 @@ class CSVParser(FileParser): def parse(self, file): file = io.TextIOWrapper(file, encoding='utf-8') reader = csv.reader(file) + yield from ExcelParser.parse_excel_csv_reader(reader) + + +class ExcelParser(FileParser): + def parse(self, file): + excel_book = pyexcel.iget_book(file_type="xlsx", file_content=file.read()) + # Handle multiple sheets + for sheet_name in excel_book.sheet_names(): + reader = excel_book[sheet_name].to_array() + yield from self.parse_excel_csv_reader(reader) + + @staticmethod + def parse_excel_csv_reader(reader): columns = next(reader) data = [] + if len(columns) == 1 and columns[0] != 'text': + data.append({'text': columns[0]}) for i, row in enumerate(reader, start=2): if len(data) >= settings.IMPORT_BATCH_SIZE: yield data data = [] - if len(row) == len(columns) and len(row) >= 2: + # Only text column + if len(row) == len(columns) and len(row) == 1: + data.append({'text': row[0]}) + # Text, labels and metadata columns + elif len(row) == len(columns) and len(row) >= 2: text, label = row[:2] meta = json.dumps(dict(zip(columns[2:], row[2:]))) j = {'text': text, 'labels': [label], 'meta': meta} @@ -346,7 +366,6 @@ class JSONParser(FileParser): data = [] try: j = json.loads(line) - #j = json.loads(line.decode('utf-8')) j['meta'] = json.dumps(j.get('meta', {})) data.append(j) except json.decoder.JSONDecodeError: @@ -373,6 +392,7 @@ class JSONLRenderer(JSONRenderer): ensure_ascii=self.ensure_ascii, allow_nan=not self.strict) + '\n' + class JSONPainter(object): def paint(self, documents): @@ -406,6 +426,7 @@ class JSONPainter(object): data.append(d) return data + class CSVPainter(JSONPainter): def paint(self, documents): diff --git a/app/api/views.py b/app/api/views.py index 84122181..93df4a90 100644 --- a/app/api/views.py +++ b/app/api/views.py @@ -19,7 +19,7 @@ from .models import Project, Label, Document from .permissions import IsAdminUserAndWriteOnly, IsProjectUser, IsOwnAnnotation from .serializers import ProjectSerializer, LabelSerializer, DocumentSerializer, UserSerializer from .serializers import ProjectPolymorphicSerializer -from .utils import CSVParser, JSONParser, PlainTextParser, CoNLLParser, iterable_to_io +from .utils import CSVParser, ExcelParser, JSONParser, PlainTextParser, CoNLLParser, iterable_to_io from .utils import JSONLRenderer from .utils import JSONPainter, CSVPainter @@ -235,6 +235,8 @@ class TextUploadAPI(APIView): return JSONParser() elif file_format == 'conll': return CoNLLParser() + elif file_format == 'excel': + return ExcelParser() else: raise ValidationError('format {} is invalid.'.format(file_format)) diff --git a/app/server/static/components/examples/upload_seq2seq.xlsx b/app/server/static/components/examples/upload_seq2seq.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9f52fe653aa99518f3b6a158d6bb42c44a6b95b3 GIT binary patch literal 10891 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 QE;b}U{|h6iqWv}ce`Hi3n*aa+ literal 0 HcmV?d00001 diff --git a/app/server/static/components/examples/upload_text_classification.xlsx b/app/server/static/components/examples/upload_text_classification.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1e0324db9328f63aa7ec7be73a2d37840026d175 GIT binary patch literal 10044 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