diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-03-27 16:17:07 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-04-24 19:41:37 +0200 |
commit | d78001a6f707a27d5a1643179243a222057d0bda (patch) | |
tree | ec59521a9c454faef7bf4af91dc13ca4938596fd | |
parent | 33171a9b1ba785ae1ff68934509c967b1a8e4348 (diff) | |
download | Ishtar-d78001a6f707a27d5a1643179243a222057d0bda.tar.bz2 Ishtar-d78001a6f707a27d5a1643179243a222057d0bda.zip |
CSV import: manage semi-colon as separator
-rw-r--r-- | archaeological_finds/tests.py | 3 | ||||
-rw-r--r-- | archaeological_operations/tests.py | 26 | ||||
-rw-r--r-- | archaeological_operations/tests/MCC-operations-example-semi-colon.csv | 3 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 2 | ||||
-rw-r--r-- | ishtar_common/migrations/0089_import_csv_sep.py | 20 | ||||
-rw-r--r-- | ishtar_common/models_imports.py | 16 |
6 files changed, 59 insertions, 11 deletions
diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index 00a45ba6a..929cdfeff 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -423,7 +423,8 @@ class ImportFindTest(ImportTest, TestCase): 'imported_images': SimpleUploadedFile(mcc_images.name, mcc_images.read())} post_dict = {'importer_type': MCC.pk, 'skip_lines': 1, - "encoding": 'utf-8', "name": 'init_find_import'} + "encoding": 'utf-8', "name": 'init_find_import', + "csv_sep": u","} form = forms_common.NewImportForm(data=post_dict, files=file_dict, user=self.user) form.is_valid() diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index 6781cf531..3f659ef3b 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -115,7 +115,8 @@ class ImportTest(object): tg.is_set = True tg.save() - def init_ope_import(self, filename='MCC-operations-example.csv'): + def init_ope_import(self, filename='MCC-operations-example.csv', + sep=u","): mcc_operation = ImporterType.objects.get(name=u"MCC - Opérations") mcc_operation_file = open( settings.ROOT_PATH + @@ -126,7 +127,7 @@ class ImportTest(object): group, c = TargetKeyGroup.objects.get_or_create(name="My group") post_dict = {'importer_type': mcc_operation.pk, 'skip_lines': 1, "encoding": 'utf-8', "name": 'init_ope_import', - "associated_group": group.pk} + "associated_group": group.pk, "csv_sep": sep} form = forms_common.NewImportForm(data=post_dict, files=file_dict, user=self.user) form.is_valid() @@ -222,7 +223,8 @@ class ImportTest(object): file_dict = {'imported_file': SimpleUploadedFile(mcc_file.name, mcc_file.read())} post_dict = {'importer_type': mcc_parcel.pk, 'skip_lines': 1, - "encoding": 'utf-8', "name": 'init_parcel_import'} + "encoding": 'utf-8', "name": 'init_parcel_import', + "csv_sep": u","} form = forms_common.NewImportForm(data=post_dict, files=file_dict, user=self.user) form.is_valid() @@ -244,7 +246,8 @@ class ImportTest(object): file_dict = {'imported_file': SimpleUploadedFile(mcc_file.name, mcc_file.read())} post_dict = {'importer_type': mcc.pk, 'skip_lines': 1, - "encoding": 'utf-8', "name": 'init_context_record_import'} + "encoding": 'utf-8', "name": 'init_context_record_import', + 'csv_sep': u","} form = forms_common.NewImportForm(data=post_dict, files=file_dict, user=self.user) form.is_valid() @@ -318,6 +321,19 @@ class ImportOperationTest(ImportTest, TestCase): def test_import_bad_encoding(self): self.init_ope_import('MCC-operations-example-bad-encoding.csv') + def test_import_semi_colon_sep(self): + first_ope_nb = models.Operation.objects.count() + importer, form = self.init_ope_import( + 'MCC-operations-example-semi-colon.csv', sep=u";") + self.assertTrue(form.is_valid()) + impt = form.save(self.ishtar_user) + impt.initialize() + self.init_ope_targetkey(imp=impt) + impt.importation() + # new operations have now been imported + current_ope_nb = models.Operation.objects.count() + self.assertEqual(current_ope_nb, first_ope_nb + 2) + def test_keys_limitation(self): # each key association associated to the import init_ope_number = models.Operation.objects.count() @@ -537,7 +553,7 @@ class ImportDocumentTest(ImportTest, TestCase): group, c = TargetKeyGroup.objects.get_or_create(name="My group") post_dict = {'importer_type': doc_import.pk, 'skip_lines': 1, "encoding": 'utf-8', "name": 'init_ope_import', - "associated_group": group.pk} + "associated_group": group.pk, "csv_sep": u","} form = forms_common.NewImportForm(data=post_dict, files=file_dict, user=self.user) form.is_valid() diff --git a/archaeological_operations/tests/MCC-operations-example-semi-colon.csv b/archaeological_operations/tests/MCC-operations-example-semi-colon.csv new file mode 100644 index 000000000..f94ab5005 --- /dev/null +++ b/archaeological_operations/tests/MCC-operations-example-semi-colon.csv @@ -0,0 +1,3 @@ +code OA;region;type operation;intitule operation;operateur;responsable operation;date debut terrain;date fin terrain;chronologie generale;identifiant document georeferencement;notice scientifique +4201;Bourgogne;Fouille programmée;Oppìdum de Paris 2;L'opérateur;;2000/01/31;2002/12/31;Age du Fer;; +4200;Bourgogne;Fouille programmée;Oppìdum de Paris;L'opérateur;Jean Sui-Resp'on Sablé;2000/01/22;2002/12/31;Age du Fer & Gallo-Romain & Néolithik & Moderne;; diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index eb15445a4..b5423ca48 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -130,7 +130,7 @@ class NewImportForm(forms.ModelForm): model = models.Import fields = ( 'name', 'importer_type', 'imported_file', 'encoding', - 'imported_images', 'imported_images_link', + 'csv_sep', 'imported_images', 'imported_images_link', 'associated_group', 'conservative_import', 'skip_lines' ) diff --git a/ishtar_common/migrations/0089_import_csv_sep.py b/ishtar_common/migrations/0089_import_csv_sep.py new file mode 100644 index 000000000..c2cd4ac75 --- /dev/null +++ b/ishtar_common/migrations/0089_import_csv_sep.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2019-03-27 16:16 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ishtar_common', '0088_auto_20190218_1808'), + ] + + operations = [ + migrations.AddField( + model_name='import', + name='csv_sep', + field=models.CharField(choices=[(',', ','), (';', ';')], default=',', help_text='Separator for CSV file. Standard is comma but Microsoft Excel do not follow this standard and use semi-colon.', max_length=1, verbose_name='CSV separator'), + ), + ] diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py index 380a582cf..a774cef6d 100644 --- a/ishtar_common/models_imports.py +++ b/ishtar_common/models_imports.py @@ -807,6 +807,9 @@ ENCODINGS = [(settings.ENCODING, settings.ENCODING), (settings.ALT_ENCODING, settings.ALT_ENCODING), ('utf-8', 'utf-8')] +CSV_SEPS = ((u",", u","), + (u";", u";"),) + delayed_import = None delayed_check = None @@ -848,6 +851,11 @@ class Import(models.Model): ) encoding = models.CharField(_(u"Encoding"), choices=ENCODINGS, default=u'utf-8', max_length=15) + csv_sep = models.CharField( + _(u"CSV separator"), choices=CSV_SEPS, default=u',', max_length=1, + help_text=_(u"Separator for CSV file. Standard is comma but Microsoft " + u"Excel do not follow this standard and use semi-colon.") + ) skip_lines = models.IntegerField( _(u"Skip lines"), default=1, help_text=_(u"Number of header lines in your file (can be 0).")) @@ -919,7 +927,7 @@ class Import(models.Model): filename = self.imported_file.path with open(filename, 'r') as f: reader = unicodecsv.reader( - f, encoding=self.encoding) + f, encoding=self.encoding, delimiter=str(self.csv_sep)) nb = sum(1 for row in reader) - self.skip_lines self.number_of_line = nb self.save() @@ -1040,9 +1048,9 @@ class Import(models.Model): for encoding in encodings: try: with open(imported_file) as csv_file: - vals = [line - for line in unicodecsv.reader(csv_file, - encoding=encoding)] + vals = [line for line in unicodecsv.reader( + csv_file, encoding=encoding, + delimiter=str(self.csv_sep))] if tmpdir: shutil.rmtree(tmpdir) return vals |