make iconolab a django app and not a full blown django project. The project has bedd transfered to iconolab-mcc
authorymh <ymh.work@gmail.com>
Wed, 16 May 2018 00:22:05 +0200
changeset 535 4217bf54446a
parent 534 f65361a277aa
child 536 7f8390504d84
make iconolab a django app and not a full blown django project. The project has bedd transfered to iconolab-mcc
src/iconolab/__init__.py
src/iconolab/forms/comments.py
src/iconolab/settings/__init__.py
src/iconolab/settings/dev.py.tmpl
src/iconolab/urls.py
src/iconolab/wsgi.py
src/manage.py
src/requirements/base.txt
src/requirements/base.txt.in
src/requirements/dev.txt
src/requirements/prod.txt
--- a/src/iconolab/__init__.py	Mon May 14 16:45:43 2018 +0200
+++ b/src/iconolab/__init__.py	Wed May 16 00:22:05 2018 +0200
@@ -1,4 +1,4 @@
-VERSION = (0, 0, 29, "final", 0)
+VERSION = (0, 1, 0, "final", 0)
 
 VERSION_STR = ".".join(map(lambda i:"%02d" % (i,), VERSION[:2]))
 
--- a/src/iconolab/forms/comments.py	Mon May 14 16:45:43 2018 +0200
+++ b/src/iconolab/forms/comments.py	Wed May 16 00:22:05 2018 +0200
@@ -26,8 +26,8 @@
         self.fields["metacategories"].queryset = self.collection.metacategories.all()
         self.fields.pop('email')
 
-    def get_comment_create_data(self):
+    def get_comment_create_data(self, site_id=None):
         self.cleaned_data['email'] = ''
-        data = super(IconolabCommentForm, self).get_comment_create_data()
+        data = super(IconolabCommentForm, self).get_comment_create_data(site_id=site_id)
         data.update({'user_email': ''})
         return data
--- a/src/iconolab/settings/__init__.py	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-"""
-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, 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__)))
-
-STATIC_ROOT = os.path.abspath(os.path.join(BASE_DIR, '../../web/static/site'))
-MEDIA_ROOT = os.path.abspath(os.path.join(BASE_DIR, '../../web/media'))
-
-BASE_URL = ''
-STATIC_URL = '/static/'
-MEDIA_URL = '/media/'
-
-LOGIN_URL = '/account/login/'
-
-#Static path
-
-
-# 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 = [
-    'iconolab.apps.IconolabApp',
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'django.contrib.sites',
-    'django.contrib.humanize',
-    'django_comments',
-    'django_comments_xtd',
-    'haystack',
-    '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 = [
-    'django.middleware.security.SecurityMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'django.middleware.clickjacking.XFrameOptionsMiddleware',
-]
-
-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
-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/
-
-TIME_ZONE = 'UTC'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = True
-
-
-IMPORT_FIELDS_DICT = {
-    "AUTR": [],
-    "ECOLE": [],
-    "TITR": ["Titre"],
-    "DENO": [],
-    "DOM": ["Domaine"],
-    "APPL": [],
-    "PERI": ["Période"],
-    "MILL": [],
-    "TECH": [],
-    "DIMS": ["Dimensions"],
-    "EPOQ": [],
-    "LIEUX": [],
-    "DECV": [],
-    "LOCA": ["Localisation"],
-    "PHOT": ["Photo"],
-    "INV": ["No inventaire"],
-    "REF": ["REFERENCE"],
-
-}
-IMPORT_DEFAULT_FIELD_TO_FILENAME_IDENTIFIER = "INV"
-NO_IMG_CONVERSION_EXTS = [".jpg"]
-IMG_CONVERSION_EXTS = [".tif", ".tiff"]
-IMG_JPG_DEFAULT_QUALITY = 80
-PREGENERATE_THUMBNAILS_SIZES = [
-    # item_images_preview.html
-    "250x250",
-    "100x100",
-]
-IMPORT_LOG_FILE = ""
-IMPORT_LOGGER_NAME = ""
-
-DJANGO_RUNSERVER = (len(sys.argv)>1 and sys.argv[1] == 'runserver')
-
-RELEVANT_TAGS_MIN_SCORE = 3
-ACCURATE_TAGS_MIN_SCORE = 3
-
-# The different thumbnail sizes that we want to pre-generate when importing or when updating collections using commands
-# This allows to pre-calculate thumbnails for media-heavy pages such as collection_home
-
--- a/src/iconolab/settings/dev.py.tmpl	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,258 +0,0 @@
-"""
-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 logging
-import os
-
-from iconolab.settings import *
-
-CONTACT_EMAIL = 'youremail@yourprovider.fr'
-
-# 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
-
-# When JS_DEV_MODE is True, the Webpack dev server should be started
-JS_DEV_MODE = True
-STATICFILES_DIRS = [
-    os.path.join(BASE_DIR, 'static'),
-    os.path.join(BASE_DIR, 'media'),
-]
-
-if JS_DEV_MODE:
-    SRC_JS_PATH = os.path.join(BASE_DIR, '..', '..', 'src_js')
-    STATICFILES_DIRS.append(SRC_JS_PATH)
-
-
-
-BASE_URL = 'http://localhost:8000'
-if JS_DEV_MODE:
-    STATIC_URL = 'http://localhost:8001/static/'
-else:
-    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
-
-
-COMMENTS_APP = "django_comments_xtd"
-COMMENTS_XTD_MODEL = "iconolab.models.IconolabComment"
-COMMENTS_XTD_FORM_CLASS = 'iconolab.forms.comments.IconolabCommentForm'
-COMMENTS_XTD_MAX_THREAD_LEVEL = 1
-COMMENTS_PER_PAGE_DEFAULT = 10
-
-SITE_ID = 1
-
-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.template.context_processors.media',
-                'django.template.context_processors.static',
-                'django.template.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.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': '',                      # Or path to database file if using sqlite3.
-        'USER': '',                      # Not used with sqlite3.
-        'PASSWORD': '',                  # Not used with sqlite3.
-        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
-        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
-    }
-}
-
-# Logging
-
-LOG_FILE = os.path.abspath(os.path.join(BASE_DIR,"../../run/log/log.txt"))
-IMPORT_LOG_FILE = os.path.abspath(os.path.join(BASE_DIR,"../../run/log/import_log.txt"))
-IMPORT_LOGGER_NAME = "import_command"
-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',
-        },
-        'import_file': {
-            'level': LOG_LEVEL,
-            'class': 'logging.FileHandler',
-            'filename': IMPORT_LOG_FILE,
-            'formatter': 'semi-verbose',
-        }
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['file'],
-            'level': LOG_LEVEL,
-            'propagate': True,
-        },
-        'iconolab': {
-            'handlers': ['file'],
-            'level': LOG_LEVEL,
-            'propagate': True,
-        },
-        'import_command': {
-            'handlers': ['import_file'],
-            'level': LOG_LEVEL,
-            'propagate': True,
-        },
-    }
-}
-
-# Haystack configuration
-
-# Haystack only supports ElasticSearch 1.x
-# http://django-haystack.readthedocs.io/en/v2.5.1/installing_search_engines.html#elasticsearch
-
-HAYSTACK_CONNECTIONS = {
-    'default': {
-        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
-        'URL': 'http://127.0.0.1:9200/',
-        'INDEX_NAME': 'haystack',
-    },
-}
-
-# 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": [],
-    "DOM": ["Domaine"],
-    "APPL": [],
-    "PERI": ["Période"],
-    "MILL": [],
-    "TECH": [],
-    "DIMS": ["Dimensions"],
-    "EPOQ": [],
-    "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="
-
-RELEVANT_TAGS_MIN_SCORE = 3
-ACCURATE_TAGS_MIN_SCORE = 3
--- a/src/iconolab/urls.py	Mon May 14 16:45:43 2018 +0200
+++ b/src/iconolab/urls.py	Wed May 16 00:22:05 2018 +0200
@@ -1,92 +1,69 @@
 """iconolab URL Configuration
+"""
+import notifications.urls
+from django import views as django_views
+from django.conf import settings
+from django.conf.urls.static import static
+from django.contrib import admin
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.views import (password_reset, password_reset_complete,
+                                       password_reset_confirm,
+                                       password_reset_done)
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import include, path, re_path, reverse_lazy
 
-The `urlpatterns` list routes URLs to views. For more information please see:
-    https://docs.djangoproject.com/en/1.9/topics/http/urls/
-Examples:
-Function views
-    1. Add an import:  from my_app import views
-    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
-Class-based views
-    1. Add an import:  from other_app.views import Home
-    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
-Including another URLconf
-    1. Import the include() function: from django.conf.urls import url, include
-    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
-"""
-from django.urls import reverse_lazy
-from django.conf.urls import url, include
-
-from django.contrib import admin
-from django import views as django_views
 from iconolab import views
-from django.conf import settings
-
-from django.conf.urls.static import static
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.views import password_reset, password_reset_done, password_reset_confirm, password_reset_complete
-from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-import notifications.urls
 
 urlpatterns = [
-    url(r'^$', django_views.generic.RedirectView.as_view(url=reverse_lazy("home"))),
-    url(r'^admin/', admin.site.urls),
-    url(r'^home$', views.objects.GlobalHomepageView.as_view(), name="home"),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)$', views.objects.CollectionHomepageView.as_view(), name='collection_home'), # Home fond
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/items/(?P<item_guid>[^/]+)$', views.objects.ShowItemView.as_view(), name='item_detail'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/items/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)$', views.objects.ShowImageView.as_view(), name='image_detail'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/bookmark/?$', views.objects.BookmarkImageView.as_view(), name='image_bookmark'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/?$', django_views.generic.RedirectView.as_view(pattern_name="image_detail")),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/create$', login_required(views.objects.CreateAnnotationView.as_view()), name='annotation_create'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/?$', views.objects.ShowAnnotationView.as_view(), name='annotation_detail'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.objects.ShowAnnotationViewOld.as_view(), name='annotation_detail_old'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/readonly$', views.objects.ReadonlyAnnotationView.as_view(), name='annotation_readonly'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/edit$', login_required(views.objects.EditAnnotationView.as_view()), name='annotation_edit'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/?$', views.objects.ShowRevisionsView.as_view(), name='annotation_revisions'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/detail', views.objects.ShowRevisionView.as_view(), name='revision_detail'),
-    url(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/merge$', login_required(views.objects.MergeProposalView.as_view()), name='annotation_merge'),
+    path(r'home', views.objects.GlobalHomepageView.as_view(), name="home"),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)$', views.objects.CollectionHomepageView.as_view(), name='collection_home'), # Home fond
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/items/(?P<item_guid>[^/]+)$', views.objects.ShowItemView.as_view(), name='item_detail'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/items/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/?$', django_views.generic.RedirectView.as_view(pattern_name="collection_home")),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)$', views.objects.ShowImageView.as_view(), name='image_detail'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/bookmark/?$', views.objects.BookmarkImageView.as_view(), name='image_bookmark'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/?$', django_views.generic.RedirectView.as_view(pattern_name="image_detail")),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/create$', login_required(views.objects.CreateAnnotationView.as_view()), name='annotation_create'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/?$', views.objects.ShowAnnotationView.as_view(), name='annotation_detail'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/detail$', views.objects.ShowAnnotationViewOld.as_view(), name='annotation_detail_old'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/readonly$', views.objects.ReadonlyAnnotationView.as_view(), name='annotation_readonly'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/edit$', login_required(views.objects.EditAnnotationView.as_view()), name='annotation_edit'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/?$', views.objects.ShowRevisionsView.as_view(), name='annotation_revisions'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/detail', views.objects.ShowRevisionView.as_view(), name='revision_detail'),
+    re_path(r'^collections/(?P<collection_name>[a-zA-Z0-9\-]+)/images/(?P<image_guid>[^/]+)/annotations/(?P<annotation_guid>[^/]+)/revisions/(?P<revision_guid>[^/]+)/merge$', login_required(views.objects.MergeProposalView.as_view()), name='annotation_merge'),
 
-    url(r'^user/settings/?$', login_required(views.userpages.UserSettingsView.as_view()), name="user_settings"),
-    url(r'^user/collections/?$', login_required(views.userpages.UserCollectionsView.as_view()), name="user_collections"),
-    url(r'^user/notifications/', include(notifications.urls, namespace='notifications')),
-    url(r'^user/(?P<slug>[\w.@+-]+)/?$', views.userpages.UserHomeView.as_view(), name="user_home"),
-    url(r'^user/(?P<slug>[\w.@+-]+)/commented/?$', views.userpages.UserCommentedView.as_view(), name="user_commented"),
-    url(r'^user/(?P<slug>[\w.@+-]+)/contributed/?$', views.userpages.UserContributedView.as_view(), name="user_contributed"),
-    url(r'^user/(?P<slug>[\w.@+-]+)/annotations/?$', views.userpages.UserAnnotationsView.as_view(), name="user_annotations"),
-    url(r'^user/(?P<slug>[\w.@+-]+)/bookmarks/?$', views.userpages.UserBookmarksView.as_view(), name="user_bookmarks"),
-    url(r'^bookmarks/(?P<bookmark>[0-9]+)/delete/?$', views.userpages.BookmarkDeleteView.as_view(), name="bookmark_delete"),
-    url(r'^bookmarks/(?P<bookmark>[0-9]+)/edit/?$', views.userpages.BookmarkEditView.as_view(), name="bookmark_edit"),
-    url(r'^user/adminpanel/(?P<collection_name>[a-zA-Z0-9\-]+)/$', views.userpages.UserCollectionAdminView.as_view(), name="user_admin_panel"),
-    url(r'^user/notifications/all/?$', login_required(views.userpages.UserNotificationsView.as_view()), name="user_notifications"),
+    re_path(r'^user/settings/?$', login_required(views.userpages.UserSettingsView.as_view()), name="user_settings"),
+    re_path(r'^user/collections/?$', login_required(views.userpages.UserCollectionsView.as_view()), name="user_collections"),
+    path(r'user/notifications/', include(notifications.urls, namespace='notifications')),
+    re_path(r'^user/(?P<slug>[\w.@+-]+)/?$', views.userpages.UserHomeView.as_view(), name="user_home"),
+    re_path(r'^user/(?P<slug>[\w.@+-]+)/commented/?$', views.userpages.UserCommentedView.as_view(), name="user_commented"),
+    re_path(r'^user/(?P<slug>[\w.@+-]+)/contributed/?$', views.userpages.UserContributedView.as_view(), name="user_contributed"),
+    re_path(r'^user/(?P<slug>[\w.@+-]+)/annotations/?$', views.userpages.UserAnnotationsView.as_view(), name="user_annotations"),
+    re_path(r'^user/(?P<slug>[\w.@+-]+)/bookmarks/?$', views.userpages.UserBookmarksView.as_view(), name="user_bookmarks"),
+    re_path(r'^bookmarks/(?P<bookmark>[0-9]+)/delete/?$', views.userpages.BookmarkDeleteView.as_view(), name="bookmark_delete"),
+    re_path(r'^bookmarks/(?P<bookmark>[0-9]+)/edit/?$', views.userpages.BookmarkEditView.as_view(), name="bookmark_edit"),
+    re_path(r'^user/adminpanel/(?P<collection_name>[a-zA-Z0-9\-]+)/$', views.userpages.UserCollectionAdminView.as_view(), name="user_admin_panel"),
+    re_path(r'^user/notifications/all/?$', login_required(views.userpages.UserNotificationsView.as_view()), name="user_notifications"),
 
-    url(r'^errors/404', views.misc.NotFoundErrorView.as_view(), name="404error"),
-
-    url(r'^help/', views.misc.HelpView.as_view(), name="iconolab_help"),
-    url(r'^glossary/', views.misc.GlossaryView.as_view(), name="iconolab_glossary"),
-    url(r'^credits/', views.misc.CreditsView.as_view(), name="iconolab_credits"),
-    url(r'^contributioncharter/', views.misc.ContributionCharterView.as_view(), name="iconolab_charter"),
-    url(r'^legalmentions/', views.misc.LegalMentionsView.as_view(), name="iconolab_legals"),
+    path(r'errors/404', views.misc.NotFoundErrorView.as_view(), name="404error"),
 
-    url(r'^account/', include('iconolab.auth.urls', namespace='account')),
-    url(r'^password/reset$', password_reset, name='password_reset'),
-    url(r'^password/reset/done$', password_reset_done, name='password_reset_done'),
-    url(r'^password/reset/complete$', password_reset_complete, name='password_reset_complete'),
-    url(r'^password/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', password_reset_confirm, name='password_reset_confirm'),
+    path(r'help/', views.misc.HelpView.as_view(), name="iconolab_help"),
+    path(r'glossary/', views.misc.GlossaryView.as_view(), name="iconolab_glossary"),
+    path(r'credits/', views.misc.CreditsView.as_view(), name="iconolab_credits"),
+    path(r'contributioncharter/', views.misc.ContributionCharterView.as_view(), name="iconolab_charter"),
+    path(r'legalmentions/', views.misc.LegalMentionsView.as_view(), name="iconolab_legals"),
 
-    url(r'^search/', include('iconolab.search_indexes.urls', namespace='search_indexes')),
-    url(r'^comments/', include('django_comments_xtd.urls')),
-    url(r'^comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment"),
-    url(r'^comments/annotation/(?P<annotation_guid>[^/]+)/comment-form$', views.comments.get_comment_form, name="get_comment_form"),
-    url(r'^comments/annotation/(?P<annotation_guid>[^/]+)/comments.json$', views.comments.get_annotation_comments_json, name="get_annotation_comments_json"),
+    path(r'account/', include('iconolab.auth.urls', namespace='account')),
+    path(r'password/reset', password_reset, name='password_reset'),
+    path(r'password/reset/done', password_reset_done, name='password_reset_done'),
+    path(r'password/reset/complete', password_reset_complete, name='password_reset_complete'),
+    re_path(r'^password/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', password_reset_confirm, name='password_reset_confirm'),
 
-    url(r'^compare/$', views.objects.TestView.as_view(), name="compare_view")
-    #url(r'^search/', include('haystack.urls'), name="search_iconolab"),
-]
-
+    path(r'search/', include('iconolab.search_indexes.urls', namespace='search_indexes')),
+    path(r'comments/', include('django_comments_xtd.urls')),
+    path(r'comments/annotation/post', views.comments.post_comment_iconolab, name="post_comment"),
+    re_path(r'^comments/annotation/(?P<annotation_guid>[^/]+)/comment-form$', views.comments.get_comment_form, name="get_comment_form"),
+    re_path(r'^comments/annotation/(?P<annotation_guid>[^/]+)/comments.json$', views.comments.get_annotation_comments_json, name="get_annotation_comments_json"),
 
-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)
+    path(r'compare/', views.objects.TestView.as_view(), name="compare_view")
+]
--- a/src/iconolab/wsgi.py	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-"""
-WSGI config for iconolab project.
-
-It exposes the WSGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
-"""
-
-import os
-
-from django.core.wsgi import get_wsgi_application
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iconolab.settings.dev")
-
-application = get_wsgi_application()
--- a/src/manage.py	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-import os
-import sys
-
-if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iconolab.settings.dev")
-
-    from django.core.management import execute_from_command_line
-
-    execute_from_command_line(sys.argv)
--- a/src/requirements/base.txt	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-Django==2.0.5
-django-comments-xtd==2.1.0
-django-contrib-comments==1.8.0
-django-haystack==2.8.1
-#django-notifications-hq==1.4.0a0
-git+https://github.com/django-notifications/django-notifications.git#egg=django-notifications-hq
-djangorestframework==3.8.2
-elasticsearch==6.2.0
-Pillow==5.1.0
-requests==2.18.4
-sorl-thumbnail==12.4.1
--- a/src/requirements/base.txt.in	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-# must run "pip install -r base.txt.in" in src/requirements
--e ..
--- a/src/requirements/dev.txt	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
--r base.txt
-setuptools_scm
--- a/src/requirements/prod.txt	Mon May 14 16:45:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
--r base.txt
-pylibmc
-uWSGI
-psycopg2 --no-binary psycopg2