Move importcollection and importmetacategories commands to the generic project. Add unique constraint to Metacategories
authorymh <ymh.work@gmail.com>
Wed, 27 Jun 2018 15:50:14 +0200
changeset 556 3fd4e04004f6
parent 555 907d5bfef8d6
child 557 7301141078de
Move importcollection and importmetacategories commands to the generic project. Add unique constraint to Metacategories
src/iconolab/__init__.py
src/iconolab/management/commands/importcollection.py
src/iconolab/management/commands/importmetacategories.py
src/iconolab/migrations/0031_auto_20180627_1320.py
src/iconolab/models.py
--- a/src/iconolab/__init__.py	Tue Jun 26 16:06:53 2018 +0200
+++ b/src/iconolab/__init__.py	Wed Jun 27 15:50:14 2018 +0200
@@ -1,6 +1,6 @@
 VERSION = (0, 1, 2, "final", 0)
 
-VERSION_STR = ".".join(map(lambda i:"%02d" % (i,), VERSION[:2]))
+VERSION_STR = ".".join(map(lambda i: "%02d" % (i,), VERSION[:2]))
 
 ###
 # https://github.com/django/django/blob/1.9.1/django/utils/version.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/management/commands/importcollection.py	Wed Jun 27 15:50:14 2018 +0200
@@ -0,0 +1,104 @@
+# -*- coding: UTF-8 -*-
+import imghdr
+import json
+import logging
+import os
+from contextlib import ExitStack
+from io import BytesIO
+
+from django.conf import settings
+from django.core.management.base import CommandError
+from django.db import transaction
+from PIL import Image as ImagePIL
+
+from iconolab.management.commands.importimages import BaseImportImagesCommand
+from iconolab.models import Collection
+
+if settings.IMPORT_LOGGER_NAME and settings.LOGGING['loggers'].get(settings.IMPORT_LOGGER_NAME, ''):
+    logger = logging.getLogger(settings.IMPORT_LOGGER_NAME)
+else:
+    logger = logging.getLogger(__name__)
+
+class Command(BaseImportImagesCommand):
+    help = 'import collection image and file from a directory'
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'source_json',
+            help='creates a new collection from a json file, must be an object with fields : '
+                 '"name" (identifier), '
+                 '"verbose_name" (proper title name), '
+                 '"description" (description on homepage, html is supported), '
+                 '"image" (image on homepages, is a path to a image file. A relative path is relative to the json file path") '
+        )
+        parser.add_argument(
+            '--collection-image',
+            dest='collection_image',
+            default=False,
+            help='The path to an image. override the one given in the json file'
+        )
+
+        parser.add_argument(
+            '--no-jpg-conversion',
+            action='store_true',
+            dest='no-jpg-conversion',
+            default=False,
+            help='use this option if you only want the image copied and not converted'
+        )
+
+    def handle(self, *args, **options):
+
+        print('# Logging with logger '+logger.name)
+        logger.debug('# Initializing command with args: %r', options)
+
+        print('# Checking collection args')
+        collection_json_path = options.get('source_json')
+        if not os.path.isfile(collection_json_path):
+            print('### No %s json file was found in the source directory' % collection_json_path)
+            raise CommandError('!!! Json file '+ collection_json_path +' was not found !!!')
+
+        print('## Loading collection json')
+        with open(collection_json_path) as json_fixture_file:
+            collection_data = json.loads(json_fixture_file.read())
+            coll_name = collection_data.get('name')
+            if not coll_name:
+                print('!!! Collection data key "name" is empty')
+                raise CommandError('!!! Collection data key "name" is empty')
+            if Collection.objects.filter(name=coll_name).exists():
+                print(
+                    '!!! A Collection with the provided name already exists!')
+                raise CommandError('!!! A Collection with the provided name already exists!')
+
+        with transaction.atomic():
+            collection = Collection.objects.create(
+                name=coll_name,
+                verbose_name=collection_data.get('verbose_name', coll_name),
+                description=collection_data.get('description', coll_name),
+            )
+
+            image_path = options.get('collection_image', None)
+
+            if not image_path:
+                image_path = collection_data.get('image')
+                if image_path and not os.path.isabs(image_path):
+                    image_path = os.path.join(
+                        os.path.dirname(os.path.abspath(collection_json_path)),
+                        image_path)
+
+
+            _, ext = os.path.splitext(image_path)
+            target_filename = "%s_image%s" % (coll_name, ext)
+
+            content_image = None
+            with ExitStack() as stack:
+                if not options.get('no-jpg-conversion', False) and imghdr.what(image_path) != 'jpeg':
+                    target_filename = "%s_image%s" % (coll_name, '.jpeg')
+                    print("Convert image from %s to %s" % (image_path, target_filename))
+                    image_convert = ImagePIL.open(image_path)
+                    if not image_convert.mode == 'RGB':
+                        image_convert = image_convert.convert('RGB')
+                    content_image = stack.enter_context(BytesIO())
+                    image_convert.save(content_image, "JPEG", quality=settings.IMG_JPG_DEFAULT_QUALITY)
+                else:
+                    content_image = stack.enter_context(open(image_path, 'rb'))
+                collection.image.save(target_filename, content_image)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/management/commands/importmetacategories.py	Wed Jun 27 15:50:14 2018 +0200
@@ -0,0 +1,70 @@
+# -*- coding: UTF-8 -*-
+import json
+import logging
+import os
+
+from django.conf import settings
+from django.core.management.base import CommandError
+from django.db import transaction
+
+from iconolab.management.commands.importimages import BaseImportImagesCommand
+from iconolab.models import Collection, MetaCategory
+
+if settings.IMPORT_LOGGER_NAME and settings.LOGGING['loggers'].get(settings.IMPORT_LOGGER_NAME, ''):
+    logger = logging.getLogger(settings.IMPORT_LOGGER_NAME)
+else:
+    logger = logging.getLogger(__name__)
+
+class Command(BaseImportImagesCommand):
+    help = 'import metacategories files from a directory'
+
+    def add_arguments(self, parser):
+        parser.add_argument('metacategories_json')
+
+        parser.add_argument(
+            '--collection-id',
+            dest='collection_id',
+            default=False,
+            help='insert extracted data into the specified collection instead of trying to load a collection fixture. the id is either a integer or a collection name',
+        )
+
+    def handle(self, *args, **options):
+
+        print('# Logging with logger '+logger.name)
+        logger.debug('# Initializing command with args: %r', options)
+
+        collection = None
+        collection_id = options.get('collection_id')
+
+        if not collection_id:
+            raise CommandError('!!! No collection fixture or collection id, aborting because we can\'t properly generate data. !!!')
+
+        print('## Finding collection with id ' + collection_id)
+
+        try:
+            try:
+                collection = Collection.objects.get(pk=int(collection_id))
+            except ValueError:
+                collection = Collection.objects.get(name=collection_id)
+        except Collection.DoesNotExist:
+            raise CommandError('!!! Collection with primary key ' + collection_id
+                            +' was not found, aborting !!!')
+
+        metacategories_json_path = options.get('metacategories_json')
+        if not os.path.isfile(metacategories_json_path):
+            print('### No '+metacategories_json_path+'.json file was found')
+            raise CommandError(
+                '!!! Fixture file '+metacategories_json_path+' was not found !!!')
+
+        with open(metacategories_json_path) as metacategories_json_file, transaction.atomic():
+            metacategories_data = json.load(metacategories_json_file)
+            for metacategory in metacategories_data:
+                if metacategory.get('label', None) is None:
+                    raise CommandError(
+                        '!!! Metacategory without label !!!')
+                MetaCategory.objects.create(
+                    collection=collection,
+                    label=metacategory.get('label'),
+                    triggers_notifications=metacategory.get(
+                        'triggers_notifications', 0)
+                )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/migrations/0031_auto_20180627_1320.py	Wed Jun 27 15:50:14 2018 +0200
@@ -0,0 +1,17 @@
+# Generated by Django 2.0.6 on 2018-06-27 13:20
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('iconolab', '0030_auto_20180601_1609'),
+    ]
+
+    operations = [
+        migrations.AlterUniqueTogether(
+            name='metacategory',
+            unique_together={('collection', 'label')},
+        ),
+    ]
--- a/src/iconolab/models.py	Tue Jun 26 16:06:53 2018 +0200
+++ b/src/iconolab/models.py	Wed Jun 27 15:50:14 2018 +0200
@@ -127,7 +127,7 @@
 
     item = models.OneToOneField('Item', related_name='metadatas', on_delete=models.PROTECT)
 
-    natural_key = models.CharField(max_length=1024, default="")
+    natural_key = models.CharField(max_length=1024, default="", unique=True)
 
     """
     JSON field integration
@@ -969,6 +969,9 @@
         (DISAGREEMENT, 'disagreement'),
     )
 
+    class Meta:
+        unique_together = (("collection", "label"),)
+
     collection = models.ForeignKey(Collection, related_name="metacategories", on_delete=models.PROTECT)
     label = models.CharField(max_length=255)
     triggers_notifications = models.IntegerField(