# HG changeset patch # User ymh # Date 1530107414 -7200 # Node ID 3fd4e04004f6e49ef3647e9789da433ae8f50aee # Parent 907d5bfef8d66e18cb3faea88fdabf3c722be654 Move importcollection and importmetacategories commands to the generic project. Add unique constraint to Metacategories diff -r 907d5bfef8d6 -r 3fd4e04004f6 src/iconolab/__init__.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 diff -r 907d5bfef8d6 -r 3fd4e04004f6 src/iconolab/management/commands/importcollection.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) diff -r 907d5bfef8d6 -r 3fd4e04004f6 src/iconolab/management/commands/importmetacategories.py --- /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) + ) diff -r 907d5bfef8d6 -r 3fd4e04004f6 src/iconolab/migrations/0031_auto_20180627_1320.py --- /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')}, + ), + ] diff -r 907d5bfef8d6 -r 3fd4e04004f6 src/iconolab/models.py --- 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(