diff options
author | Étienne Loks <etienne.loks@proxience.com> | 2015-05-05 13:03:09 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@proxience.com> | 2015-05-05 13:05:06 +0200 |
commit | 3c34bd06079b29d7af91ec15ad912e1faf60ecbb (patch) | |
tree | 333db30d0b6bbb112d77c32ca1b878c8f70ee69a | |
parent | bdc9cdab0d5e314b5d1a33a2d87fca22138d649d (diff) | |
download | Ishtar-3c34bd06079b29d7af91ec15ad912e1faf60ecbb.tar.bz2 Ishtar-3c34bd06079b29d7af91ec15ad912e1faf60ecbb.zip |
Imports: manage importation interface, error file
-rw-r--r-- | Makefile.example | 18 | ||||
-rw-r--r-- | example_project/urls.py | 6 | ||||
-rw-r--r-- | ishtar_common/data_importer.py | 7 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 16 | ||||
-rw-r--r-- | ishtar_common/migrations/0028_auto__chg_field_targetkey_key__chg_field_targetkey_value.py | 319 | ||||
-rw-r--r-- | ishtar_common/models.py | 73 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/import_list.html | 8 | ||||
-rw-r--r-- | ishtar_common/views.py | 8 |
8 files changed, 433 insertions, 22 deletions
diff --git a/Makefile.example b/Makefile.example index 7d37d6bc3..82f2a42e3 100644 --- a/Makefile.example +++ b/Makefile.example @@ -16,6 +16,8 @@ clean: -find . -name '*.pyc' -exec rm {} \; -find . -name '.*.swp' -exec rm {} \; +update: clean syncdb compilemessages collectstatic + test: clean cd $(project); $(PYTHON) manage.py test $(apps) @@ -78,3 +80,19 @@ compilemessages: cd $(CURDIR)/$$DIR; \ $(PYTHON) ../$(project)/manage.py compilemessages; \ done + +collectstatic: + cd $(project);$(PYTHON) manage.py collectstatic --noinput + +schemamigrations: + cd $(project);\ + for APP in $(apps); do \ + echo "* schemamigration for "$$APP; \ + $(PYTHON) manage.py schemamigration --auto $$APP; \ + done + +generate_doc: + cd $(project);\ + $(PYTHON) manage.py graph_models --pydot -I "ImporterType,ImporterDefault,ImporterDefaultValues,ImporterColumn,Regexp,ImportTarget,FormaterType,Import" ishtar_common > /tmp/ishtar.dot + dot -Tpng /tmp/ishtar.dot -o docs/source/_static/db-imports.png + rm /tmp/ishtar.dot diff --git a/example_project/urls.py b/example_project/urls.py index e6ea1f37e..0c3b2a07f 100644 --- a/example_project/urls.py +++ b/example_project/urls.py @@ -30,3 +30,9 @@ urlpatterns += patterns('ishtar_common.views', urlpatterns += patterns('', (r'^admin/', include(admin.site.urls)), ) + +if settings.DEBUG: + # static files (images, css, javascript, etc.) + urlpatterns += patterns('', + (r'^media/(?P<path>.*)$', 'django.views.static.serve', { + 'document_root': settings.MEDIA_ROOT})) diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index cef98789e..24c8e166d 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -277,7 +277,7 @@ class StrChoiceFormater(Formater): if output == 'db' and self.db_target: from ishtar_common.models import TargetKey for missing in self.missings: - q = {'target':self.db_target, 'value':missing} + q = {'target':self.db_target, 'key':missing} if TargetKey.objects.filter(**q).count(): continue with transaction.commit_on_success(): @@ -409,7 +409,7 @@ class StrToBoolean(Formater): from ishtar_common.models import TargetKey for missing in self.missings: try: - q = {'target':self.db_target, 'value':missing} + q = {'target':self.db_target, 'key':missing} models.TargetKey.objects.create(**q) except IntegrityError: pass @@ -853,7 +853,8 @@ class Importer(object): def get_csv_errors(self): if not self.errors: return "" - csv_errors = ["line,col,error"] + csv_errors = ['"%s","%s","%s"' % (unicode(_("line")), + unicode(_("col")), unicode(_("error")))] for line, col, error in self.errors: csv_errors.append(u'"%s","%s","%s"' % (line and unicode(line) or '-', col and unicode(col) or '-', diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 677656af6..d8740b68c 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -101,10 +101,10 @@ class NewImportForm(forms.ModelForm): class TargetKeyForm(forms.ModelForm): class Meta: model = models.TargetKey - fields = ('target', 'value', 'key') + fields = ('target', 'key', 'value') widgets = { - 'value': forms.TextInput(attrs={'readonly':'readonly'}), - 'key': forms.Select(), + 'key': forms.TextInput(attrs={'readonly':'readonly'}), + 'value': forms.Select(), } def __init__(self, *args, **kwargs): @@ -112,8 +112,8 @@ class TargetKeyForm(forms.ModelForm): instance = getattr(self, 'instance', None) if instance and instance.pk: self.fields['target'].widget.attrs['readonly'] = True - self.fields['value'].widget.attrs['readonly'] = True - self.fields['key'].widget.choices = list(instance.target.get_choices()) + self.fields['key'].widget.attrs['readonly'] = True + self.fields['value'].widget.choices = list(instance.target.get_choices()) def clean_target(self): instance = getattr(self, 'instance', None) @@ -122,12 +122,12 @@ class TargetKeyForm(forms.ModelForm): else: return self.cleaned_data['target'] - def clean_value(self): + def clean_key(self): instance = getattr(self, 'instance', None) if instance and instance.pk: - return instance.value + return instance.key else: - return self.cleaned_data['value'] + return self.cleaned_data['key'] def save(self, commit=True): super(TargetKeyForm, self).save(commit) diff --git a/ishtar_common/migrations/0028_auto__chg_field_targetkey_key__chg_field_targetkey_value.py b/ishtar_common/migrations/0028_auto__chg_field_targetkey_key__chg_field_targetkey_value.py new file mode 100644 index 000000000..b99480a2b --- /dev/null +++ b/ishtar_common/migrations/0028_auto__chg_field_targetkey_key__chg_field_targetkey_value.py @@ -0,0 +1,319 @@ +# -*- 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): + + # Changing field 'TargetKey.key' + db.alter_column('ishtar_common_targetkey', 'key', self.gf('django.db.models.fields.TextField')(default=1)) + + # Changing field 'TargetKey.value' + db.alter_column('ishtar_common_targetkey', 'value', self.gf('django.db.models.fields.TextField')(null=True)) + + def backwards(self, orm): + + # Changing field 'TargetKey.key' + db.alter_column('ishtar_common_targetkey', 'key', self.gf('django.db.models.fields.TextField')(null=True)) + + # User chose to not deal with backwards NULL issues for 'TargetKey.value' + raise RuntimeError("Cannot reverse this migration. 'TargetKey.value' and its values cannot be restored.") + + 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', [], {}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'keys'", 'to': "orm['ishtar_common.ImportTarget']"}), + 'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + '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 24b64bec0..20b85e614 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -22,6 +22,7 @@ Models description """ from cStringIO import StringIO import copy +import csv import datetime from PIL import Image from importlib import import_module @@ -33,6 +34,7 @@ import unicodecsv from django.conf import settings from django.core.cache import cache from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.core.files import File from django.core.files.uploadedfile import SimpleUploadedFile from django.core.validators import validate_slug from django.core.urlresolvers import reverse, NoReverseMatch @@ -51,11 +53,12 @@ from django.contrib import admin from simple_history.models import HistoricalRecords as BaseHistoricalRecords +from ishtar_common.unicode_csv import UnicodeWriter from ishtar_common.ooo_replace import ooo_replace from ishtar_common.model_merging import merge_model_objects from ishtar_common.utils import get_cache -from ishtar_common.data_importer import Importer, ImportFormater, IntegerFormater, \ - FloatFormater, UnicodeFormater, DateFormater, TypeFormater +from ishtar_common.data_importer import Importer, ImportFormater, \ + IntegerFormater, FloatFormater, UnicodeFormater, DateFormater, TypeFormater def post_save_user(sender, **kwargs): user = kwargs['instance'] @@ -388,7 +391,7 @@ class GeneralType(models.Model): self.add_key(key) def get_keys(self): - keys = [] + keys = [self.txt_idx] content_type = ContentType.objects.get_for_model(self.__class__) for ik in ItemKey.objects.filter(content_type=content_type, object_id=self.pk).all(): @@ -1168,7 +1171,8 @@ class ImportTarget(models.Model): self.target.split('__')) def get_choices(self): - if not self.associated_model or not hasattr(self.associated_model, 'get_types'): + if not self.associated_model or not hasattr(self.associated_model, + 'get_types'): return [] return self.associated_model.get_types() @@ -1179,8 +1183,8 @@ class TargetKey(models.Model): them in ItemKey table """ target = models.ForeignKey(ImportTarget, related_name='keys') - key = models.TextField(_(u"Key"), blank=True, null=True) - value = models.TextField(_(u"Value")) + key = models.TextField(_(u"Key")) + value = models.TextField(_(u"Value"), blank=True, null=True) is_set = models.BooleanField(_(u"Is set"), default=False) class Meta: @@ -1189,6 +1193,29 @@ class TargetKey(models.Model): def __unicode__(self): return u" - ".join([unicode(self.target), self.key[:50]]) + def save(self, *args, **kwargs): + obj = super(TargetKey, self).save(*args, **kwargs) + if not self.value: + return obj + associated_model = self.target.associated_model + if associated_model and hasattr(self.target.associated_model, + "add_key"): + v = None + # pk is given + try: + pk = int(self.value) + v = self.target.associated_model.objects.get( + pk=unicode(self.value)) + except (ValueError, self.target.associated_model.DoesNotExist): + # try with txt_idx + try: + v = self.target.associated_model.objects.get( + txt_idx=unicode(self.value)) + except (self.target.associated_model.DoesNotExist): + pass + if v: + v.add_key(self.key) + return obj TARGET_MODELS = [ ('OrganizationType', _(u"Organization type")), ('SourceType', _(u"Source type")), @@ -1279,7 +1306,10 @@ IMPORT_STATE = (("C", _(u"Created")), ("A", _(u"Analysed")), ("P", _(u"Import pending")), ("IP", _(u"Import in progress")), - ("F", _(u"Finished"))) + ("FE", _(u"Finished with errors")), + ("F", _(u"Finished")), + ("AC", _(u"Archived")), + ) IMPORT_STATE_DCT = dict(IMPORT_STATE) @@ -1325,6 +1355,8 @@ class Import(models.Model): if self.state == 'A': actions.append(('A', _(u"Re-analyse"))) actions.append(('I', _(u"Launch import"))) + if self.state in ('F', 'FE'): + actions.append(('AC', _(u"Archive"))) actions.append(('D', _(u"Delete"))) return actions @@ -1363,6 +1395,33 @@ class Import(models.Model): self.state = 'A' self.save() + def importation(self): + self.state = 'IP' + self.save() + importer = self.get_importer_instance() + importer.importation(self.data_table) + if importer.errors: + self.state = 'FE' + error_file = None + + filename = slugify(self.importer_type.name) + ".csv" + now = datetime.datetime.now().isoformat('-' + ).replace(':','') + error_file = '.'.join(filename.split('.')[:-1]) \ + + "_errors_%s.csv" % now + error_file = os.sep.join([self.error_file.storage.location, + error_file]) + with open(error_file, 'w') as fle: + fle.write(importer.get_csv_errors().encode('utf-8')) + self.error_file = File(open(fle.name)) + else: + self.state = 'F' + self.save() + + def archive(self): + self.state = 'AC' + self.save() + class Organization(Address, Merge, OwnPerms, ValueGetter): TABLE_COLS = ('name', 'organization_type',) name = models.CharField(_(u"Name"), max_length=300) diff --git a/ishtar_common/templates/ishtar/import_list.html b/ishtar_common/templates/ishtar/import_list.html index 76fc1cd72..40bc019ad 100644 --- a/ishtar_common/templates/ishtar/import_list.html +++ b/ishtar_common/templates/ishtar/import_list.html @@ -11,9 +11,10 @@ <table> <tr> <th>{% trans "Type" %}</th> - <th>{% trans "Filename" %}</th> + <th>{% trans "File" context "file" %}</th> <th>{% trans "Creation" %}</th> <th>{% trans "Status" %}</th> + <th>{% trans "Action" %}</th> </tr> {% for import in object_list %} <tr> @@ -21,7 +22,7 @@ {{import.importer_type}} </td> <td> - {{import.imported_filename}} + <a href='{{MEDIA_URL}}{{import.imported_file}}'>{% trans "Source file" %}</a> </td> <td> {{import.creation_date}} @@ -42,6 +43,9 @@ <a href='{% url "import_link_unmatched" import.importer_type.pk %}'>{% trans "Link unmatched items" %}</a> {% endif %} </td> + <td>{% if import.error_file %} + <a href='{{MEDIA_URL}}{{import.error_file}}'>{% trans "Error file" %}</a> + {% endif %}</td> </tr> {% endfor %} </table> diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 0d7389949..b005659db 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -886,7 +886,7 @@ class ImportListView(IshtarMixin, LoginRequiredMixin, ListView): def get_queryset(self): user = models.IshtarUser.objects.get(pk=self.request.user.pk) - return self.model.objects.filter(user=user).exclude(state='F' + return self.model.objects.filter(user=user).exclude(state='AC' ).order_by('-creation_date') def post(self, request, *args, **kwargs): @@ -908,13 +908,17 @@ class ImportListView(IshtarMixin, LoginRequiredMixin, ListView): imprt.delete() elif action == 'A': imprt.initialize() + elif action == 'I': + imprt.importation() + elif action == 'AC': + imprt.archive() return HttpResponseRedirect(reverse(self.current_url)) class ImportOldListView(ImportListView): current_url = 'old_imports' def get_queryset(self): user = models.IshtarUser.objects.get(pk=self.request.user.pk) - return self.model.objects.filter(user=user, state='F' + return self.model.objects.filter(user=user, state='AC' ).order_by('-creation_date') class ImportLinkView(IshtarMixin, LoginRequiredMixin, ModelFormSetView): |