diff options
author | Étienne Loks <etienne.loks@proxience.com> | 2015-05-03 16:02:46 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@proxience.com> | 2015-05-03 16:02:46 +0200 |
commit | 40ffb48d6075a14a9591d6a7c83f40ef5afe97e5 (patch) | |
tree | 1f80cbb46ef1df0be2a42c34320e2ea8e507e805 | |
parent | 3eb00c5048bafcabf7643155426398a2b887cf0d (diff) | |
download | Ishtar-40ffb48d6075a14a9591d6a7c83f40ef5afe97e5.tar.bz2 Ishtar-40ffb48d6075a14a9591d6a7c83f40ef5afe97e5.zip |
Import: add management of link between user data and db data
-rw-r--r-- | ishtar_common/data_importer.py | 74 | ||||
-rw-r--r-- | ishtar_common/migrations/0026_auto__add_targetkey__add_unique_targetkey_target_value__add_field_impo.py | 340 | ||||
-rw-r--r-- | ishtar_common/models.py | 106 | ||||
-rw-r--r-- | ishtar_common/tests.py | 1 |
4 files changed, 489 insertions, 32 deletions
diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index c2924a541..194a9a5fa 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -88,6 +88,9 @@ class ImporterError(Exception): return self.msg class Formater(object): + def __init__(self, *args, **kwargs): + self.db_target = kwargs.get('db_target', None) + def format(self, value): return value @@ -95,8 +98,10 @@ class Formater(object): return class UnicodeFormater(Formater): - def __init__(self, max_length, clean=False, re_filter=None, notnull=False): + def __init__(self, max_length, clean=False, re_filter=None, notnull=False, + db_target=None): self.max_length = max_length + self.db_target = db_target self.clean = clean self.re_filter = re_filter self.notnull = notnull @@ -182,12 +187,13 @@ class IntegerFormater(Formater): class StrChoiceFormater(Formater): def __init__(self, choices, strict=False, equiv_dict={}, model=None, - cli=False, many_split=''): + cli=False, many_split='', db_target=None): self.choices = list(choices) self.strict = strict self.equiv_dict = copy.deepcopy(equiv_dict) self.cli = cli self.model = model + self.db_target = db_target self.create = False self.missings = set() self.many_split = many_split @@ -200,6 +206,17 @@ class StrChoiceFormater(Formater): if model and v: v = model.objects.get(pk=v) self.equiv_dict[value] = v + if self.db_target: + for target_key in self.db_target.keys.filter(is_set=True).all(): + value = target_key.value + if not self.strict: + value = slugify(value) + if value in self.equiv_dict: + continue + v = target_key.key + if model and v: + v = model.objects.get(pk=v) + self.equiv_dict[value] = v def prepare(self, value): return unicode(value).strip() @@ -257,6 +274,13 @@ class StrChoiceFormater(Formater): unicode(self.equiv_dict[value]))) else: self.equiv_dict[value] = None + if output == 'db' and self.db_target: + for missing in missings: + try: + q = {'target':self.db_target, 'value':missing} + models.TargetKey.objects.create(**q) + except IntegrityError: + pass def new(self, value): return @@ -272,12 +296,14 @@ class StrChoiceFormater(Formater): return self.equiv_dict[value] class TypeFormater(StrChoiceFormater): - def __init__(self, model, cli=False, defaults={}, many_split=False): + def __init__(self, model, cli=False, defaults={}, many_split=False, + db_target=None): self.create = True self.strict = False self.model = model self.defaults = defaults self.many_split = many_split + self.db_target = db_target self.missings = set() self.equiv_dict, self.choices = {}, [] for item in model.objects.all(): @@ -304,8 +330,9 @@ class TypeFormater(StrChoiceFormater): return self.model.objects.create(**values) class DateFormater(Formater): - def __init__(self, date_format="%d/%m/%Y"): + def __init__(self, date_format="%d/%m/%Y", db_target=None): self.date_format = date_format + self.db_target = db_target def format(self, value): value = value.strip() @@ -318,11 +345,26 @@ class DateFormater(Formater): 'value':value}) class StrToBoolean(Formater): - def __init__(self, choices={}, cli=False, strict=False): + def __init__(self, choices={}, cli=False, strict=False, db_target=None): self.dct = copy.copy(choices) self.cli = cli self.strict= strict + self.db_target = db_target self.missings = set() + if self.db_target: + for target_key in self.db_target.keys.filter(is_set=True).all(): + value = target_key.value + value = self.prepare(value) + if value in self.dct: + continue + v = target_key.key + if v in ('False', '0'): + v = False + elif v: + v = True + else: + v = None + self.dct[value] = v def prepare(self, value): value = unicode(value).strip() @@ -359,6 +401,13 @@ class StrToBoolean(Formater): self.dct[value] = False else: self.dct[value] = None + if output == 'db' and self.db_target: + for missing in missings: + try: + q = {'target':self.db_target, 'value':missing} + models.TargetKey.objects.create(**q) + except IntegrityError: + pass def format(self, value): value = self.prepare(value) @@ -388,8 +437,9 @@ class Importer(object): } def __init__(self, skip_lines=0, reference_header=None, - check_col_num=False, test=False, check_validity=True, - history_modifier=None, output='silent'): + check_col_num=False, test=False, check_validity=True, + history_modifier=None, output='silent', + importer_instance=None): """ * skip_line must be set if the data provided has got headers lines. * a reference_header can be provided to perform a data compliance @@ -408,14 +458,18 @@ class Importer(object): self.check_col_num = check_col_num self.check_validity = check_validity self.line_format = copy.copy(self.LINE_FORMAT) + self.importer_instance = importer_instance self._initialized = False self._defaults = self.DEFAULTS.copy() self.history_modifier = history_modifier self.output = output if not self.history_modifier: - # get the first admin - self.history_modifier = User.objects.filter(is_superuser=True - ).order_by('pk')[0] + if self.importer_instance: + self.history_modifier = self.importer_instance.user + else: + # import made by the CLI: get the first admin + self.history_modifier = User.objects.filter( + is_superuser=True).order_by('pk')[0] def initialize(self, table, output='silent'): """ diff --git a/ishtar_common/migrations/0026_auto__add_targetkey__add_unique_targetkey_target_value__add_field_impo.py b/ishtar_common/migrations/0026_auto__add_targetkey__add_unique_targetkey_target_value__add_field_impo.py new file mode 100644 index 000000000..b4752a48e --- /dev/null +++ b/ishtar_common/migrations/0026_auto__add_targetkey__add_unique_targetkey_target_value__add_field_impo.py @@ -0,0 +1,340 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'TargetKey' + db.create_table('ishtar_common_targetkey', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('target', self.gf('django.db.models.fields.related.ForeignKey')(related_name='keys', to=orm['ishtar_common.ImporterColumn'])), + ('key', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('value', self.gf('django.db.models.fields.TextField')()), + ('is_set', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal('ishtar_common', ['TargetKey']) + + # Adding unique constraint on 'TargetKey', fields ['target', 'value'] + db.create_unique('ishtar_common_targetkey', ['target_id', 'value']) + + # Adding field 'Import.skip_lines' + db.add_column('ishtar_common_import', 'skip_lines', + self.gf('django.db.models.fields.IntegerField')(default=1), + keep_default=False) + + + # Changing field 'Import.creation_date' + db.alter_column('ishtar_common_import', 'creation_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, null=True)) + + def backwards(self, orm): + # Removing unique constraint on 'TargetKey', fields ['target', 'value'] + db.delete_unique('ishtar_common_targetkey', ['target_id', 'value']) + + # Deleting model 'TargetKey' + db.delete_table('ishtar_common_targetkey') + + # Deleting field 'Import.skip_lines' + db.delete_column('ishtar_common_import', 'skip_lines') + + + # Changing field 'Import.creation_date' + db.alter_column('ishtar_common_import', 'creation_date', self.gf('django.db.models.fields.DateTimeField')(null=True)) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'ishtar_common.arrondissement': { + 'Meta': {'object_name': 'Arrondissement'}, + 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Department']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}) + }, + 'ishtar_common.author': { + 'Meta': {'object_name': 'Author'}, + 'author_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.AuthorType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'author'", 'to': "orm['ishtar_common.Person']"}) + }, + 'ishtar_common.authortype': { + 'Meta': {'object_name': 'AuthorType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'ishtar_common.canton': { + 'Meta': {'object_name': 'Canton'}, + 'arrondissement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Arrondissement']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}) + }, + 'ishtar_common.department': { + 'Meta': {'ordering': "['number']", 'object_name': 'Department'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + 'number': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}) + }, + 'ishtar_common.documenttemplate': { + 'Meta': {'ordering': "['associated_object_name', 'name']", 'object_name': 'DocumentTemplate'}, + 'associated_object_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'template': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + }, + 'ishtar_common.format': { + 'Meta': {'object_name': 'Format'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'ishtar_common.formatertype': { + 'Meta': {'unique_together': "(('formater_type', 'options', 'many_split'),)", 'object_name': 'FormaterType'}, + 'formater_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'many_split': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'options': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.globalvar': { + 'Meta': {'ordering': "['slug']", 'object_name': 'GlobalVar'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.historicalorganization': { + 'Meta': {'ordering': "('-history_date', '-history_id')", 'object_name': 'HistoricalOrganization'}, + 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'history_creator_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'history_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'history_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'history_modifier_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'history_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}), + 'history_user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'blank': 'True'}), + 'merge_key': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'organization_type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.import': { + 'Meta': {'object_name': 'Import'}, + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), + 'end_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'error_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'imported_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.ImporterType']"}), + 'result_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'seconds_remaining': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'skip_lines': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'state': ('django.db.models.fields.CharField', [], {'default': "'C'", 'max_length': '2'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.IshtarUser']"}) + }, + 'ishtar_common.importercolumn': { + 'Meta': {'object_name': 'ImporterColumn'}, + 'col_number': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'columns'", 'to': "orm['ishtar_common.ImporterType']"}), + 'regexp_pre_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Regexp']", 'null': 'True', 'blank': 'True'}), + 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'ishtar_common.importerdefault': { + 'Meta': {'object_name': 'ImporterDefault'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'importer_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'defaults'", 'to': "orm['ishtar_common.ImporterType']"}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.importerdefaultvalues': { + 'Meta': {'object_name': 'ImporterDefaultValues'}, + 'default_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'default_values'", 'to': "orm['ishtar_common.ImporterDefault']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.importerduplicatefield': { + 'Meta': {'object_name': 'ImporterDuplicateField'}, + 'column': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'duplicate_fields'", 'to': "orm['ishtar_common.ImporterColumn']"}), + 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'ishtar_common.importertype': { + 'Meta': {'object_name': 'ImporterType'}, + 'associated_models': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_template': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ishtar_common.IshtarUser']", 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.importtarget': { + 'Meta': {'object_name': 'ImportTarget'}, + 'column': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'targets'", 'to': "orm['ishtar_common.ImporterColumn']"}), + 'formater_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.FormaterType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'regexp_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Regexp']", 'null': 'True', 'blank': 'True'}), + 'target': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.ishtaruser': { + 'Meta': {'object_name': 'IshtarUser', '_ormbases': ['auth.User']}, + 'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ishtaruser'", 'unique': 'True', 'to': "orm['ishtar_common.Person']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'ishtar_common.itemkey': { + 'Meta': {'object_name': 'ItemKey'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'importer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Import']", 'null': 'True', 'blank': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}) + }, + 'ishtar_common.organization': { + 'Meta': {'object_name': 'Organization'}, + 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'history_creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'merge_candidate': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_candidate_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Organization']"}), + 'merge_exclusion': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_exclusion_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Organization']"}), + 'merge_key': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'organization_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.OrganizationType']"}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.organizationtype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'OrganizationType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'ishtar_common.person': { + 'Meta': {'object_name': 'Person'}, + 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'address_complement': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'attached_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'members'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['ishtar_common.Organization']"}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'history_creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'history_modifier': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'merge_candidate': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_candidate_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Person']"}), + 'merge_exclusion': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'merge_exclusion_rel_+'", 'null': 'True', 'to': "orm['ishtar_common.Person']"}), + 'merge_key': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'mobile_phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'person_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ishtar_common.PersonType']", 'symmetrical': 'False'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '18', 'null': 'True', 'blank': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'raw_name': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'}), + 'surname': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}), + 'town': ('django.db.models.fields.CharField', [], {'max_length': '70', 'null': 'True', 'blank': 'True'}) + }, + 'ishtar_common.persontype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'PersonType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'ishtar_common.regexp': { + 'Meta': {'object_name': 'Regexp'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'regexp': ('django.db.models.fields.CharField', [], {'max_length': '500'}) + }, + 'ishtar_common.sourcetype': { + 'Meta': {'object_name': 'SourceType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'ishtar_common.supporttype': { + 'Meta': {'object_name': 'SupportType'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'txt_idx': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'ishtar_common.targetkey': { + 'Meta': {'unique_together': "(('target', 'value'),)", 'object_name': 'TargetKey'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_set': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'key': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'keys'", 'to': "orm['ishtar_common.ImporterColumn']"}), + 'value': ('django.db.models.fields.TextField', [], {}) + }, + 'ishtar_common.town': { + 'Meta': {'ordering': "['numero_insee']", 'object_name': 'Town'}, + 'canton': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Canton']", 'null': 'True', 'blank': 'True'}), + 'center': ('django.contrib.gis.db.models.fields.PointField', [], {'srid': '27572', 'null': 'True', 'blank': 'True'}), + 'departement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ishtar_common.Department']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'numero_insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '6'}), + 'surface': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['ishtar_common']
\ No newline at end of file diff --git a/ishtar_common/models.py b/ishtar_common/models.py index f2193eae2..1f059f2f5 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -28,6 +28,7 @@ from importlib import import_module import os import re import tempfile +import unicodecsv from django.conf import settings from django.core.cache import cache @@ -955,13 +956,6 @@ class OrganizationType(GeneralType): verbose_name_plural = _(u"Organization types") ordering = ('label',) -IMPORT_STATE = (("C", _(u"Created")), - ("AP", _(u"Analyse in progress")), - ("A", _(u"Analysed")), - ("P", _(u"Import pending")), - ("IP", _(u"Import in progress")), - ("F", _(u"Finished"))) - MODELS = [ ('archaeological_operations.models.Operation', _(u"Operation")), ('archaeological_operations.models.Parcel', _(u"Parcels")), @@ -985,6 +979,9 @@ def get_model_fields(model): return fields class ImporterType(models.Model): + """ + Description of a table to be mapped with ishtar database + """ name = models.CharField(_(u"Name"), blank=True, null=True, max_length=100) description = models.CharField(_(u"Description"), blank=True, null=True, @@ -1022,7 +1019,7 @@ class ImporterType(models.Model): LINE_FORMAT.append(None) continue for target in column.targets.all(): - ft = target.formater_type.get_formater_type() + ft = target.formater_type.get_formater_type(target) if not ft: continue formater_types.append(ft) @@ -1032,8 +1029,9 @@ class ImporterType(models.Model): formater_kwargs['regexp'] = re.compile( column.regexp_pre_filter.regexp) formater_kwargs['duplicate_fields'] = [field.field_name - for field in column.duplicate_fields.all()] - formater = ImportFormater(targets, formater_types, **formater_kwargs) + for field in column.duplicate_fields.all()] + formater = ImportFormater(targets, formater_types, + **formater_kwargs) LINE_FORMAT.append(formater) args = {'OBJECT_CLS':OBJECT_CLS, 'DESC':self.description, 'DEFAULTS':DEFAULTS, 'LINE_FORMAT':LINE_FORMAT} @@ -1041,6 +1039,9 @@ class ImporterType(models.Model): return newclass class ImporterDefault(models.Model): + """ + Targets of default values in an import + """ importer_type = models.ForeignKey(ImporterType, related_name='defaults') target = models.CharField(u"Target", max_length=500) class Meta: @@ -1071,6 +1072,9 @@ class ImporterDefault(models.Model): return values class ImporterDefaultValues(models.Model): + """ + Default values in an import + """ default_target = models.ForeignKey(ImporterDefault, related_name='default_values') target = models.CharField(u"Target", max_length=500) @@ -1096,6 +1100,9 @@ class ImporterDefaultValues(models.Model): return "" class ImporterColumn(models.Model): + """ + Import file column description + """ importer_type = models.ForeignKey(ImporterType, related_name='columns') col_number = models.IntegerField(_(u"Column number"), default=1) regexp_pre_filter = models.ForeignKey("Regexp", blank=True, null=True) @@ -1105,6 +1112,9 @@ class ImporterColumn(models.Model): verbose_name_plural = _(u"Importer - Columns") class ImporterDuplicateField(models.Model): + """ + Direct copy of result in other fields + """ column = models.ForeignKey(ImporterColumn, related_name='duplicate_fields') field_name = models.CharField(_(u"Field name"), blank=True, null=True, max_length=200) @@ -1124,6 +1134,9 @@ class Regexp(models.Model): IMPORTER_TYPES = [] class ImportTarget(models.Model): + """ + Ishtar database target for a column + """ column = models.ForeignKey(ImporterColumn, related_name='targets') target = models.CharField(u"Target", max_length=500) regexp_filter = models.ForeignKey("Regexp", blank=True, null=True) @@ -1132,6 +1145,26 @@ class ImportTarget(models.Model): verbose_name = _(u"Importer - Target") verbose_name_plural = _(u"Importer - Targets") + def __unicode__(self): + return u" - ".join([unicode(self.column), self.target[:50]]) + +class TargetKey(models.Model): + """ + User's link between import source and ishtar database. + Also temporary used for GeneralType to point missing link before adding + them in ItemKey table + """ + target = models.ForeignKey(ImporterColumn, related_name='keys') + key = models.TextField(_(u"Key"), blank=True, null=True) + value = models.TextField(_(u"Value")) + is_set = models.BooleanField(_(u"Is set"), default=False) + + class Meta: + unique_together = ('target', 'value') + + def __unicode__(self): + return u" - ".join([unicode(self.target), self.key[:50]]) + TARGET_MODELS = [ ('OrganizationType', _(u"Organization type")), ('SourceType', _(u"Source type")), @@ -1190,10 +1223,10 @@ class FormaterType(models.Model): if self.format_type in IMPORTER_TYPES_CHOICES: return IMPORTER_TYPES_CHOICES[self.format_type] - def get_formater_type(self): + def get_formater_type(self, target): if self.formater_type not in IMPORTER_TYPES_DCT.keys(): return - kwargs = {} + kwargs = {'target':target} if self.many_split: kwargs['many_split'] = self.many_split if self.formater_type == 'TypeFormater': @@ -1206,35 +1239,44 @@ class FormaterType(models.Model): model = import_module(self.options) return TypeFormater(model, **kwargs) elif self.formater_type == 'IntegerFormater': - return IntegerFormater() + return IntegerFormater(**kwargs) elif self.formater_type == 'FloatFormater': - return FloatFormater() + return FloatFormater(**kwargs) elif self.format_type == 'UnicodeFormater': try: - return UnicodeFormater(int(self.options.strip())) + return UnicodeFormater(int(self.options.strip()), **kwargs) except ValueError: return elif self.format_type == 'DateFormater': - return DateFormater(self.options) + return DateFormater(self.options, **kwargs) + +IMPORT_STATE = (("C", _(u"Created")), + ("AP", _(u"Analyse in progress")), + ("A", _(u"Analysed")), + ("P", _(u"Import pending")), + ("IP", _(u"Import in progress")), + ("F", _(u"Finished"))) class Import(models.Model): user = models.ForeignKey('IshtarUser') importer_type = models.ForeignKey(ImporterType) imported_file = models.FileField(_(u"Imported file"), upload_to="upload/imports/") + skip_lines = models.IntegerField(default=1) error_file = models.FileField(_(u"Error file"), upload_to="upload/imports/", blank=True, null=True) result_file = models.FileField(_(u"Result file"), upload_to="upload/imports/", blank=True, null=True) - state = models.CharField(_(u"State"), max_length=2, choices=IMPORT_STATE) - creation_date = models.DateTimeField(_(u"Creation date"), blank=True, - null=True) + state = models.CharField(_(u"State"), max_length=2, choices=IMPORT_STATE, + default='C') + creation_date = models.DateTimeField(_(u"Creation date"), auto_now_add=True, + blank=True, null=True) end_date = models.DateTimeField(_(u"End date"), blank=True, - null=True) + null=True, editable=False) seconds_remaining = models.IntegerField(_(u"Seconds remaining"), blank=True, - null=True) + null=True, editable=False) class Meta: verbose_name = _(u"Import") verbose_name_plural = _(u"Imports") @@ -1243,6 +1285,28 @@ class Import(models.Model): return u"%s - %s" % (unicode(self.importer_type), unicode(self.user)) + @property + def importer_instance(self): + return self.importer_type.importer_class(skip_lines=self.skip_lines, + import_instance=self) + + @property + def data_table(self): + encodings = [settings.ENCODING, settings.ALT_ENCODING, 'utf-8'] + with open(self.imported_file.filename) as csv_file: + for encoding in encodings: + try: + return [line for line in unicodecsv.reader(csv_file, + encoding=encoding)] + except UnicodeDecodeError: + if encoding != encodings[-1]: + csv_file.seek(0) + continue + return [] + + def initialize(self): + self.importer_instance.initialize(self.data_table, output='db') + class Organization(Address, Merge, OwnPerms, ValueGetter): TABLE_COLS = ('name', 'organization_type',) name = models.CharField(_(u"Name"), max_length=300) diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index 4b4f38e4a..8745b079d 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -103,7 +103,6 @@ class MergeTest(TestCase): self.assertTrue(self.person_types[1] in self.person_3.person_types.all()) class ImportKeyTest(TestCase): - def testKeys(self): content_type = ContentType.objects.get_for_model(models.OrganizationType) |