prepare for publication
authorymh <ymh.work@gmail.com>
Mon, 29 Aug 2016 16:19:16 +0200
changeset 155 c1d03ab4baad
parent 154 79b70254a5e0
child 157 3abe2e799391
prepare for publication
src/iconolab/management/commands/importimages.py
src/iconolab/settings/__init__.py
src/iconolab/settings/dev.py.tmpl
src/iconolab/templates/iconolab/home.html
src/iconolab/urls.py
src/iconolab/views/iconolab.py
src/requirements/base.txt
src/requirements/base.txt.in
--- a/src/iconolab/management/commands/importimages.py	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/iconolab/management/commands/importimages.py	Mon Aug 29 16:19:16 2016 +0200
@@ -8,15 +8,22 @@
 
 class Command(BaseCommand):
     help = "import images from a directory into the media folder and creates item and image objects"
-    
+
     def add_arguments(self, parser):
         parser.add_argument("csv_path")
         parser.add_argument(
+            '--jpeg-quality',
+            dest='jpeg_quality',
+            default=settings.IMG_JPG_DEFAULT_QUALITY,
+            help='Jpeg default quality'
+
+        )
+        parser.add_argument(
             '--encoding',
             dest='encoding',
             default='utf-8',
             help='CSV file encoding'
-        
+
         )
         parser.add_argument(
             '--collection-fixture',
@@ -36,7 +43,13 @@
             default=False,
             help='add metacategories to the created collection from a fixture file',
         )
-    
+        parser.add_argument(
+            '--delimiter',
+            dest='csv_delimiter',
+            default=';',
+            help='csv file delimiter'
+        )
+
     def handle(self, *args, **options):
         pp = pprint.PrettyPrinter(indent=4)
         try:
@@ -57,7 +70,7 @@
                         if collection_data[0]["model"] != "iconolab.Collection":
                             raise ValueError("!!! Collection fixture should provide one iconolab.Collection object and nothing else. !!!")
                 except ValueError as e:
-                    raise ValueError("!!! JSON Data is invalid. !!!")    
+                    raise ValueError("!!! JSON Data is invalid. !!!")
             elif options.get("collection_id"):
                 print("## Finding collection with id "+options.get("collection_id"))
                 try:
@@ -65,8 +78,8 @@
                 except Collection.DoesNotExist:
                     raise ValueError("!!! Collection with primary key "+options.get("collection_id")+" was not found, aborting !!!")
             else:
-                raise ValueError("!!! No collection fixture or collection id, aborting because we can't properly generate data. !!!") 
-            
+                raise ValueError("!!! No collection fixture or collection id, aborting because we can't properly generate data. !!!")
+
             if options.get("metacategories_fixture"):
                 print("## Finding metacategories fixture json data in "+source_dir)
                 metacategories_fixture_path = os.path.join(source_dir, options.get("metacategories_fixture"))
@@ -81,9 +94,9 @@
                             raise ValueError("!!! The fixture should only contain metacategories for the imported collection !!!")
                         elif options.get("collection_id") and metacategory["fields"].get("collection", False) != collection.id:
                             raise ValueError("!!! The fixture should only contain metacategories for the imported collection !!!")
-            
+
             # We read the csv
-            csvreader = csv.DictReader(open(options.get("csv_path"), encoding=options.get("encoding")), delimiter=";")
+            csvreader = csv.DictReader(open(options.get("csv_path"), encoding=options.get("encoding")), delimiter=options.get('csv_delimiter', ';'))
             print("# Extracting data from csv file and storing it in standardized format")
             # We store data using the Jocondelab keys, as defined in settings.IMPORT_FIELDS_DICT
             cleaned_csv_data=[]
@@ -96,7 +109,7 @@
                             cleaned_row_data[key] = row[row_key]
                             break
                 cleaned_csv_data.append(cleaned_row_data)
-            
+
             print("# Finding corresponding images and filtering csv data for found images")
             # Listing image files in csv directory
             image_list = [f for f in os.listdir(source_dir) if os.path.isfile(os.path.join(source_dir, f)) and not f.endswith(".csv")]
@@ -128,7 +141,7 @@
                             col_im = ImagePIL.open(os.path.join(source_dir, collection_image_name))
                             print("##### Generating or copying jpeg for "+collection_image_name)
                             col_im.thumbnail(col_im.size)
-                            col_im.save(collection_image_path, "JPEG", quality=100)
+                            col_im.save(collection_image_path, "JPEG", quality=options.get("jpeg_quality", settings.IMG_JPG_DEFAULT_QUALITY))
                         except Exception as e:
                             print(e)
             if options.get("metacategories_fixture"):
@@ -138,6 +151,9 @@
             print("### Images will be stored in "+target_dir)
             for item in filtered_csv_data:
                 print("#### Computing metadatas for item "+item["INV"]+" (inv number)")
+                if not item["INV"]:
+                    print("#### No INV number, skipping")
+                    continue
                 item_authors = item["AUTR"]
                 item_school = item["ECOLE"]
                 item_designation = ""
@@ -195,16 +211,18 @@
                                 im_width, im_height = im.size
                             except Exception as e:
                                 print(e)
+                                continue
                         else:
                             jpeg_img_path = image_path
                             try:
                                 im = ImagePIL.open(os.path.join(source_dir, image))
                                 print("##### Generating or copying jpeg for "+image)
                                 im.thumbnail(im.size)
-                                im.save(jpeg_img_path, "JPEG", quality=100)
+                                im.save(jpeg_img_path, "JPEG", quality=options.get("jpeg_quality", settings.IMG_JPG_DEFAULT_QUALITY))
                                 im_width, im_height = im.size
                             except Exception as e:
                                 print(e)
+                                continue
                         new_image = Image.objects.create(
                             item = item_object,
                             media = "uploads/"+image_name+".jpg",
@@ -220,4 +238,3 @@
             print("!!! File "+options.get("csv_path")+" does not exist. !!!")
         except ValueError as e:
             print(str(e))
- 
\ No newline at end of file
--- a/src/iconolab/settings/__init__.py	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/iconolab/settings/__init__.py	Mon Aug 29 16:19:16 2016 +0200
@@ -10,7 +10,7 @@
 https://docs.djangoproject.com/en/1.9/ref/settings/
 """
 
-import os, logging
+import os, logging, sys
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -223,3 +223,7 @@
 }
 NO_IMG_CONVERSION_EXTS = [".jpg"]
 IMG_CONVERSION_EXTS = [".tif", ".tiff"]
+
+IMG_JPG_DEFAULT_QUALITY = 80
+
+DJANGO_RUNSERVER = (sys.argv[1] == 'runserver')
--- a/src/iconolab/settings/dev.py.tmpl	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/iconolab/settings/dev.py.tmpl	Mon Aug 29 16:19:16 2016 +0200
@@ -1,266 +1,250 @@
-"""
-Django settings for iconolab project.
-
-Generated by 'django-admin startproject' using Django 1.9.5.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.9/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.9/ref/settings/
-"""
-
-import os
-
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
-STATIC_ROOT = os.path.join(BASE_DIR, '../../web/static/site')
-MEDIA_ROOT = os.path.join(BASE_DIR, '../../web/media')
-
-# dev_mode useful for src_js
-# We need to add 'iconolab.utils.context_processors.env' to context processor
-
-SRC_JS_PATH = os.path.join(BASE_DIR, '..', '..', 'src_js')
-DEV_MODE = True
-
-STATICFILES_DIRS = [
-    os.path.join(BASE_DIR, 'static'),
-    os.path.join(BASE_DIR, 'media'),
-    SRC_JS_PATH,
-]
-
-
-
-STATICFILES_DIRS = [
-    os.path.join(BASE_DIR, 'static'),
-]
-BASE_URL = ''
-STATIC_URL = '/static/'
-MEDIA_URL = '/media/'
-
-LOGIN_URL = '/account/login/'
-
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '#8)+upuo3vc7fi15czxz53ml7*(1__q8hg=m&+9ylq&st1_kqv'
-
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
-THUMBNAIL_DEBUG = True
-
-ALLOWED_HOSTS = []
-
-
-# Application definition
-
-INSTALLED_APPS = [
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'django.contrib.sites',
-    'django_comments',
-    'django_comments_xtd',
-    'haystack',
-    'iconolab.apps.IconolabApp',
-    'sorl.thumbnail',
-    'notifications'
-]
-
-
-
-COMMENTS_APP = "django_comments_xtd"
-COMMENTS_XTD_MODEL = "iconolab.models.IconolabComment"
-COMMENTS_XTD_FORM_CLASS = 'iconolab.forms.comments.IconolabCommentForm'
-COMMENTS_XTD_MAX_THREAD_LEVEL = 100
-COMMENTS_PER_PAGE_DEFAULT = 10
-
-SITE_ID = 1
-
-MIDDLEWARE_CLASSES = [
-    'django.middleware.security.SecurityMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    'reversion.middleware.RevisionMiddleware',
-]
-
-STATICFILES_DIRS = [
-    os.path.join(BASE_DIR, 'static'),
-    os.path.join(BASE_DIR, 'iconolab', 'media'),
-]
-
-MEDIA_URL = os.path.join(BASE_DIR, 'iconolab', 'media') + "/"
-
-ROOT_URLCONF = 'iconolab.urls'
-
-TEMPLATES = [
-    {
-        'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [os.path.join(BASE_DIR,'iconolab','templates')],
-        'APP_DIRS': True,
-        'OPTIONS': {
-            'context_processors': [
-                'django.template.context_processors.debug',
-                'django.template.context_processors.request',
-                'django.contrib.auth.context_processors.auth',
-                'django.contrib.messages.context_processors.messages',
-                'django.core.context_processors.media',
-                'django.core.context_processors.static',
-                'django.core.context_processors.i18n',
-                'iconolab.utils.context_processors.env',
-            ],
-        },
-    },
-]
-
-WSGI_APPLICATION = 'iconolab.wsgi.application'
-
-
-# Database
-# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
-    }
-}
-
-# Logging
-
-LOG_FILE = os.path.abspath(os.path.join(BASE_DIR,"../../run/log/log.txt"))
-LOG_LEVEL = logging.DEBUG
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': True,
-    'filters': {
-        'require_debug_false': {
-            '()': 'django.utils.log.RequireDebugFalse'
-        }
-    },
-    'formatters' : {
-        'simple' : {
-            'format': "%(asctime)s - %(levelname)s : %(message)s",
-        },
-        'semi-verbose': {
-            'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
-        },
-    },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'filters': ['require_debug_false'],
-            'class': 'django.utils.log.AdminEmailHandler'
-        },
-        'stream_to_console': {
-            'level': LOG_LEVEL,
-            'class': 'logging.StreamHandler'
-        },
-        'file': {
-            'level': LOG_LEVEL,
-            'class': 'logging.FileHandler',
-            'filename': LOG_FILE,
-            'formatter': 'semi-verbose',
-        },
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['file'],
-            'level': LOG_LEVEL,
-            'propagate': True,
-        },
-        'iconolab': {
-            'handlers': ['file'],
-            'level': LOG_LEVEL,
-            'propagate': True,
-        },
-    }
-}
-
-
-# Haystack connection
-HAYSTACK_CONNECTIONS = {
-    'default': {
-        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
-        'URL': 'http://127.0.0.1:9200/',
-        'INDEX_NAME': 'haystack',
-    },
-}
-
-# HAYSTACK_SIGNAL_PROCESSOR
-HAYSTACK_SIGNAL_PROCESSOR = 'iconolab.search_indexes.signals.RevisionSignalProcessor'
-
-CACHES = {
-    'default': {
-        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
-        'LOCATION': os.path.join(MEDIA_ROOT, 'cache'),
-#        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
-#        'LOCATION': 'unix:/var/run/memcached/memcached.socket',
-#        'KEY_PREFIX': 'ldt',
-    }
-}
-# Password validation
-# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
-
-AUTH_PASSWORD_VALIDATORS = [
-    {
-        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
-    },
-]
-
-
-# Internationalization
-# https://docs.djangoproject.com/en/1.9/topics/i18n/
-
-LANGUAGE_CODE = 'en-us'
-
-TIME_ZONE = 'UTC'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = True
-
-
-IMPORT_FIELDS_DICT = {
-    "AUTR": [],
-    "ECOLE": [],
-    "TITR": ["Titre"],
-    "DENO": [],
-    "APPL": [],
-    "PERI": ["Période"],
-    "MILL": [],
-    "EPOCH": [],
-    "TECH": [],
-    "DIMS": ["Dimensions"],
-    "EPOCH": [],
-    "LIEUX": [],
-    "DECV": [],
-    "LOCA": ["Localisation"],
-    "PHOT": ["Photo"],
-    "INV": ["No inventaire",],
-    "REF": ["REFERENCE"],
-}
-
-INTERNAL_TAGS_URL = BASE_URL
-JOCONDE_NOTICE_BASE_URL = "http://www.culture.gouv.fr/public/mistral/joconde_fr?ACTION=CHERCHER&FIELD_98=REF&VALUE_98="
+"""
+Django settings for iconolab project.
+
+Generated by 'django-admin startproject' using Django 1.9.5.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/1.9/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/1.9/ref/settings/
+"""
+from iconolab.settings import *
+
+import os, logging
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+STATIC_ROOT = os.path.join(BASE_DIR, '../../web/static/site')
+MEDIA_ROOT = os.path.join(BASE_DIR, '../../web/media')
+
+# dev_mode useful for src_js
+# We need to add 'iconolab.utils.context_processors.env' to context processor
+
+SRC_JS_PATH = os.path.join(BASE_DIR, '..', '..', 'src_js')
+DEV_MODE = True
+
+BASE_URL = ''
+STATIC_URL = '/static/'
+MEDIA_URL = '/media/'
+
+LOGIN_URL = '/account/login/'
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = '#8)+upuo3vc7fi15czxz53ml7*(1__q8hg=m&+9ylq&st1_kqv'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+THUMBNAIL_DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'django.contrib.sites',
+    'django_comments',
+    'django_comments_xtd',
+    'haystack',
+    'iconolab.apps.IconolabApp',
+    'sorl.thumbnail',
+    'notifications'
+]
+
+
+
+COMMENTS_APP = "django_comments_xtd"
+COMMENTS_XTD_MODEL = "iconolab.models.IconolabComment"
+COMMENTS_XTD_FORM_CLASS = 'iconolab.forms.comments.IconolabCommentForm'
+COMMENTS_XTD_MAX_THREAD_LEVEL = 100
+COMMENTS_PER_PAGE_DEFAULT = 10
+
+SITE_ID = 1
+
+MIDDLEWARE_CLASSES = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+    'reversion.middleware.RevisionMiddleware',
+]
+
+
+ROOT_URLCONF = 'iconolab.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR,'iconolab','templates')],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+                'django.core.context_processors.media',
+                'django.core.context_processors.static',
+                'django.core.context_processors.i18n',
+                'iconolab.utils.context_processors.env',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'iconolab.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    }
+}
+
+# Logging
+
+LOG_FILE = os.path.abspath(os.path.join(BASE_DIR,"../../run/log/log.txt"))
+LOG_LEVEL = logging.DEBUG
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'formatters' : {
+        'simple' : {
+            'format': "%(asctime)s - %(levelname)s : %(message)s",
+        },
+        'semi-verbose': {
+            'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
+        },
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        },
+        'stream_to_console': {
+            'level': LOG_LEVEL,
+            'class': 'logging.StreamHandler'
+        },
+        'file': {
+            'level': LOG_LEVEL,
+            'class': 'logging.FileHandler',
+            'filename': LOG_FILE,
+            'formatter': 'semi-verbose',
+        },
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['file'],
+            'level': LOG_LEVEL,
+            'propagate': True,
+        },
+        'iconolab': {
+            'handlers': ['file'],
+            'level': LOG_LEVEL,
+            'propagate': True,
+        },
+    }
+}
+
+
+# Haystack connection
+HAYSTACK_CONNECTIONS = {
+    'default': {
+        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
+        'URL': 'http://127.0.0.1:9200/',
+        'INDEX_NAME': 'haystack',
+    },
+}
+
+# HAYSTACK_SIGNAL_PROCESSOR
+HAYSTACK_SIGNAL_PROCESSOR = 'iconolab.search_indexes.signals.RevisionSignalProcessor'
+
+CACHES = {
+    'default': {
+        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
+        'LOCATION': os.path.join(MEDIA_ROOT, 'cache'),
+#        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
+#        'LOCATION': 'unix:/var/run/memcached/memcached.socket',
+#        'KEY_PREFIX': 'ldt',
+    }
+}
+# Password validation
+# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/1.9/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+IMPORT_FIELDS_DICT = {
+    "AUTR": [],
+    "ECOLE": [],
+    "TITR": ["Titre"],
+    "DENO": [],
+    "APPL": [],
+    "PERI": ["Période"],
+    "MILL": [],
+    "EPOCH": [],
+    "TECH": [],
+    "DIMS": ["Dimensions"],
+    "EPOCH": [],
+    "LIEUX": [],
+    "DECV": [],
+    "LOCA": ["Localisation"],
+    "PHOT": ["Photo"],
+    "INV": ["No inventaire",],
+    "REF": ["REFERENCE"],
+}
+
+INTERNAL_TAGS_URL = BASE_URL
+JOCONDE_NOTICE_BASE_URL = "http://www.culture.gouv.fr/public/mistral/joconde_fr?ACTION=CHERCHER&FIELD_98=REF&VALUE_98="
--- a/src/iconolab/templates/iconolab/home.html	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/iconolab/templates/iconolab/home.html	Mon Aug 29 16:19:16 2016 +0200
@@ -35,9 +35,11 @@
 
 {% block footer_js %}
 <script>
-	$(".collection-container:not(.selected)").hide();
-	var selectedID = /collection\-panel\-([0-9a-z\-]+)/.exec($(".collection-container.selected").attr("id"))[1];
-	$(".btn-collection").on("click", function(e){
+  $(".collection-container:not(.selected)").hide();
+  var selectedContainerId = $(".collection-container.selected").attr("id");
+  if(selectedContainerId) {
+      var selectedID = /collection\-panel\-([0-9a-z\-]+)/.exec($(".collection-container.selected").attr("id"))[1];
+      $(".btn-collection").on("click", function(e){
 	    selectedID = /show\-collection\-([0-9a-z\-]+)/.exec($(this).attr("id"))[1];
 		if (!$(this).hasClass("btn-primary")){
 		    $(".collection-container").removeClass("selected");
@@ -47,6 +49,8 @@
 		    $(".collection-container#collection-panel-"+selectedID).show()
 		    $(".collection-container#collection-panel-"+selectedID).addClass("selected")
 		}
-	})
+	});
+  }
+
 </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- a/src/iconolab/urls.py	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/iconolab/urls.py	Mon Aug 29 16:19:16 2016 +0200
@@ -20,7 +20,7 @@
 from django import views as django_views
 from iconolab import views
 from iconolab.search_indexes.views import IconolabSearchView
-from . import settings
+from django.conf import settings
 
 from django.conf.urls.static import static
 from django.contrib.auth.decorators import login_required
@@ -51,7 +51,7 @@
     url(r'^comments/', include('django_comments_xtd.urls')),
     url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment"),
     url('^user/notifications/', include(notifications.urls, namespace='notifications')),
-    
+
     url(r'collections/(?P<collection_name>[a-z0-9\-]+)/search/(?P<model_type>[a-z0-9\-]+)', IconolabSearchView.as_view(), name="collection_with_model_search"),
 
     url(r'^search/(?P<model_type>[a-z0-9\-]+)', IconolabSearchView.as_view(), name="model_search"),
@@ -62,7 +62,9 @@
     #url(r'^search/', include('haystack.urls'), name="search_iconolab"),
 ]
 
-urlpatterns += staticfiles_urlpatterns()
-urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
-#static url
-urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+
+if settings.DJANGO_RUNSERVER:
+    urlpatterns += staticfiles_urlpatterns()
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+    #static url
+    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
--- a/src/iconolab/views/iconolab.py	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/iconolab/views/iconolab.py	Mon Aug 29 16:19:16 2016 +0200
@@ -26,7 +26,7 @@
 
 class TestView(View):
     template_name = "iconolab/compare.html"
-    
+
     def get(self, request, *args, **kwargs):
         return render(request, self.template_name)
 
@@ -35,37 +35,37 @@
 class UserHomeView(DetailView):
     model = User
     slug_field = 'id'
-    
+
     def get_context_data(self, **kwargs):
         context = super(UserHomeView, self).get_context_data(**kwargs)
         return context
-    
+
     def get(self, request, *args, **kwargs):
         self.object = self.get_object()
         context = self.get_context_data()
         profile_user = self.object
         context['profile_user'] = profile_user
         context['user_annotations'] = Annotation.objects.filter(author=profile_user).prefetch_related(
-            'current_revision', 
-            'revisions', 
-            'image', 
-            'image__item', 
+            'current_revision',
+            'revisions',
+            'image',
+            'image__item',
             'image__item__collection'
         )
         context['user_revisions_annotations'] = Annotation.objects.filter(revisions__author=profile_user).exclude(author=profile_user).prefetch_related(
-            'current_revision', 
-            'revisions', 
-            'image', 
-            'image__item', 
+            'current_revision',
+            'revisions',
+            'image',
+            'image__item',
             'image__item__collection'
         ).distinct()
         comments_annotations_str_id = IconolabComment.objects.filter(user=profile_user, content_type__app_label='iconolab', content_type__model='annotation').values_list('object_pk', flat=True)
         comments_annotations_id = [int(str_id) for str_id in comments_annotations_str_id]
         context['user_comments_annotations'] = Annotation.objects.filter(id__in=comments_annotations_id).exclude(author=profile_user).exclude(annotation_guid__in=context['user_revisions_annotations'].values_list('annotation_guid', flat=True)).prefetch_related(
-            'current_revision', 
-            'revisions', 
-            'image', 
-            'image__item', 
+            'current_revision',
+            'revisions',
+            'image',
+            'image__item',
             'image__item__collection'
         ).distinct()
         if request.user.is_authenticated() and self.object == request.user:
@@ -75,7 +75,7 @@
         return render(request, 'iconolab/user_home.html', context)
 
 class UserNotificationsView(View):
-    
+
     def get(self, request, *args, **kwargs):
         context = {}
         notifications = Notification.objects.filter(recipient=request.user)
@@ -98,6 +98,7 @@
             Returns a boolean depending on wether (True) or not (False) the objects were found and a tuple containing the objects, with a select_related/prefetch_related on relevant related objects
             following this ordering: (collection, item, image, annotation, revision)
         '''
+
         objects_tuple = ()
         if 'collection_name' in kwargs.keys():
             try:
@@ -136,30 +137,30 @@
         context = super(CollectionHomepageView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
         context['collection'] = collection
-        
+
         # Recent annotations
         context['recent_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
-            'current_revision', 
+            'current_revision',
             'stats'
         ).order_by('-current_revision__created')
-        
+
         # Recent annotations
         context['revised_annotations'] = Annotation.objects.filter(image__item__collection__name=collection.name).prefetch_related(
-            'current_revision', 
+            'current_revision',
             'stats'
         ).annotate(revision_count=Count("revisions")).order_by('-revision_count')
-        
+
         contrib_calls_annotations_ids = list(set(MetaCategoryInfo.objects.filter(
-            metacategory__collection__name=collection.name, 
+            metacategory__collection__name=collection.name,
             metacategory__triggers_notifications=MetaCategory.CONTRIBUTORS
         ).order_by("comment__submit_date").values_list("comment__object_pk", flat=True)))
-        
+
         collection_annotations = Annotation.objects.filter(id__in=contrib_calls_annotations_ids).all()
         collection_ann_dict = dict([(str(annotation.id), annotation) for annotation in collection_annotations])
         context["contribution_calls_annotations_list"] = [collection_ann_dict[id] for id in contrib_calls_annotations_ids]
-        
+
         return render(request, 'iconolab/collection_home.html', context)
-    
+
 
 
 class ShowItemView(View, ContextMixin, IconolabObjectView):
@@ -192,15 +193,15 @@
         context['collection'] = collection
         context['image'] = image
         return render(request, 'iconolab/detail_image.html', context)
-    
+
 class CreateAnnotationView(View, ContextMixin, IconolabObjectView):
-    
+
     def get_context_data(self, **kwargs):
         context = super(CreateAnnotationView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
         context['image_guid'] = self.kwargs.get('image_guid', '')
         return context
-    
+
     def get(self, request, *args, **kwargs):
         success, result = self.check_kwargs(kwargs)
         if success:
@@ -212,8 +213,8 @@
         context['image'] = image
         context['form'] = annotation_form
         context['tags_data'] = '[]'
-        return render(request, 'iconolab/change_annotation.html', context) 
-    
+        return render(request, 'iconolab/change_annotation.html', context)
+
     def post(self, request, *args, **kwargs):
         success, result = self.check_kwargs(kwargs)
         if success:
@@ -249,7 +250,7 @@
         render(request, 'iconolab/change_annotation.html', context)
 
 class ShowAnnotationView(View, ContextMixin, IconolabObjectView):
-    
+
     def get_context_data(self, **kwargs):
         context = super(ShowAnnotationView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
@@ -268,7 +269,7 @@
         context['image'] = image
         context['annotation'] = annotation
         context['tags_data'] = annotation.current_revision.get_tags_json()
-        
+
         page = request.GET.get('page', 1)
         per_page = request.GET.get('perpage', 10)
         full_comments_list = IconolabComment.objects.for_app_models('iconolab.annotation').filter(object_pk = annotation.pk).order_by('thread_id', '-order')
@@ -280,12 +281,12 @@
         except EmptyPage:
             comments_list = paginator.page(paginator.num_pages)
         context['comments'] = comments_list
-        
+
         if request.user.is_authenticated():
             user_comment_notifications = Notification.objects.filter(
-                recipient=request.user, 
-                action_object_content_type__app_label='iconolab', 
-                action_object_content_type__model='iconolabcomment', 
+                recipient=request.user,
+                action_object_content_type__app_label='iconolab',
+                action_object_content_type__model='iconolabcomment',
                 target_content_type__app_label='iconolab',
                 target_content_type__model='annotation',
                 target_object_id=annotation.id
@@ -295,7 +296,7 @@
             for notification in user_comment_notifications.all():
                 if int(notification.action_object_object_id) in comment_list_ids:
                     notification.mark_as_read()
-        
+
         image.stats.views_count += 1
         image.stats.save()
         annotation.stats.views_count += 1
@@ -304,14 +305,14 @@
 
 
 class EditAnnotationView(View, ContextMixin, IconolabObjectView):
-    
+
     def get_context_data(self, **kwargs):
         context = super(EditAnnotationView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
         context['image_guid'] = self.kwargs.get('image_guid', '')
         context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
         return context
-    
+
     def get(self, request, *args, **kwargs):
         success, result = self.check_kwargs(kwargs)
         if success:
@@ -324,8 +325,8 @@
         context['annotation'] = annotation
         context['form'] = annotation_form
         context['tags_data'] = annotation.current_revision.get_tags_json()
-        return render(request, 'iconolab/change_annotation.html', context) 
-    
+        return render(request, 'iconolab/change_annotation.html', context)
+
     def post(self, request, *args, **kwargs):
         success, result = self.check_kwargs(kwargs)
         if success:
@@ -364,7 +365,7 @@
 
 
 class ShowRevisionView(View, ContextMixin, IconolabObjectView):
-    
+
     def get_context_data(self, **kwargs):
         context = super(ShowRevisionView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
@@ -372,7 +373,7 @@
         context['annotation_guid'] = self.kwargs.get('annotation_guid', '')
         context['revision_guid'] = self.kwargs.get('revision_guid', '')
         return context
- 
+
     def get(self, request, *args, **kwargs):
         success, result = self.check_kwargs(kwargs)
         if success:
@@ -388,8 +389,8 @@
         context['comment'] = revision.creation_comment.first()
         if request.user.is_authenticated() and annotation.author == request.user:
             ann_author_notified = Notification.objects.filter(
-                    recipient=request.user, 
-                    action_object_content_type__app_label='iconolab', 
+                    recipient=request.user,
+                    action_object_content_type__app_label='iconolab',
                     action_object_content_type__model='annotationrevision',
                     action_object_object_id=revision.id,
                     target_content_type__app_label='iconolab',
@@ -401,8 +402,8 @@
                 context['notified_revision'] = True
         if request.user.is_authenticated() and revision.author == request.user:
             rev_author_notified = Notification.objects.filter(
-                    recipient=request.user, 
-                    action_object_content_type__app_label='iconolab', 
+                    recipient=request.user,
+                    action_object_content_type__app_label='iconolab',
                     action_object_content_type__model='annotationrevision',
                     action_object_object_id=revision.id,
                     target_content_type__app_label='iconolab',
@@ -414,9 +415,9 @@
                 context['notified_revision'] = True
         return render(request, 'iconolab/detail_revision.html', context)
 
-        
+
 class MergeProposalView(View, ContextMixin, IconolabObjectView):
-    
+
     def get_context_data(self, **kwargs):
         context = super(MergeProposalView, self).get_context_data(**kwargs)
         context['collection_name'] = self.kwargs.get('collection_name', '')
@@ -434,10 +435,10 @@
         # Only show merge form if there is a revision to merge AND the current user is the annotation author
         if revision.state != AnnotationRevision.AWAITING or request.user != annotation.author:
             return RedirectView.as_view(
-                url=reverse('revision_detail', 
+                url=reverse('revision_detail',
                     kwargs={
-                        'collection_name': collection.name, 
-                        'image_guid': image.image_guid, 
+                        'collection_name': collection.name,
+                        'image_guid': image.image_guid,
                         'annotation_guid': annotation.annotation_guid,
                         'revision_guid': revision.revision_guid
                     }
@@ -447,27 +448,27 @@
         if 'auto_accept' in request.GET and request.GET['auto_accept'] in ['True', 'true', '1', 'yes'] and revision.parent_revision == annotation.current_revision:
             annotation.validate_existing_revision(revision)
             return RedirectView.as_view(
-                url=reverse('annotation_detail', 
+                url=reverse('annotation_detail',
                     kwargs={
-                        'collection_name': collection.name, 
-                        'image_guid': image.image_guid, 
+                        'collection_name': collection.name,
+                        'image_guid': image.image_guid,
                         'annotation_guid': annotation.annotation_guid
                     }
                 )
             )(request)
         # Auto-reject the revision only if the proper query arg is set
         if 'auto_reject' in request.GET and request.GET['auto_reject'] in ['True', 'true', '1', 'yes']:
-            annotation.reject_existing_revision(revision) 
+            annotation.reject_existing_revision(revision)
             return RedirectView.as_view(
-                url=reverse('annotation_detail', 
+                url=reverse('annotation_detail',
                     kwargs={
-                        'collection_name': collection.name, 
-                        'image_guid': image.image_guid, 
+                        'collection_name': collection.name,
+                        'image_guid': image.image_guid,
                         'annotation_guid': annotation.annotation_guid
                     }
                 )
             )(request)
-        
+
         context = self.get_context_data(**kwargs)
         context['collection'] = collection
         context['image'] = image
@@ -484,11 +485,11 @@
         context['current_revision'] = annotation.current_revision
         context['current_tags_data'] = annotation.current_revision.get_tags_json()
         context['current_comment'] = annotation.current_revision.creation_comment.first()
-        
+
         merge_form = AnnotationRevisionForm(instance=revision)
         context['merge_form'] = merge_form
         return render(request, 'iconolab/merge_revision.html', context)
-    
+
     def post(self, request, *args, **kwargs):
         # Handle merge form submit here
         success, result = self.check_kwargs(kwargs)
@@ -500,7 +501,7 @@
         image_guid = kwargs['image_guid']
         annotation_guid = kwargs['annotation_guid']
         revision_guid = kwargs['revision_guid']
-        
+
         merge_revision_form = AnnotationRevisionForm(request.POST)
         if merge_revision_form.is_valid():
             revision_title = merge_revision_form.cleaned_data['title']
@@ -537,10 +538,10 @@
         context['current_tags_data'] = annotation.current_revision.get_tags_json()
         context['current_comment'] = annotation.current_revision.creation_comment.first()
         return render(request, 'iconolab/merge_revision.html', context)
-    
-    
+
+
 class NotFoundErrorView(TemplateView):
     template_name='errors/404error.html'
-    
+
 class HelpView(TemplateView):
-    template_name='iconolab/glossary.html'
\ No newline at end of file
+    template_name='iconolab/glossary.html'
--- a/src/requirements/base.txt	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/requirements/base.txt	Mon Aug 29 16:19:16 2016 +0200
@@ -11,5 +11,5 @@
 pytz==2016.6.1
 requests==2.11.1
 six==1.10.0
-sorl-thumbnail==12.3
+sorl-thumbnail==12.4a1
 urllib3==1.16
--- a/src/requirements/base.txt.in	Fri Aug 26 13:36:05 2016 +0200
+++ b/src/requirements/base.txt.in	Mon Aug 29 16:19:16 2016 +0200
@@ -1,13 +1,13 @@
-Django >= 1.9
-django-comments-xtd
-django-contrib-comments
-django-haystack
-django-notifications-hq
-elasticsearch
-jsonfield
-Pillow
-psycopg2
-pytz
-requests
-six
-sorl-thumbnail
+Django >= 1.9
+django-comments-xtd
+django-contrib-comments
+django-haystack
+django-notifications-hq
+elasticsearch
+jsonfield
+Pillow
+psycopg2
+pytz
+requests
+six
+sorl-thumbnail >= 12.4a1