From cafd11b1faa5e91e4d15d5bed07c6b4a6ea04da0 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Mon, 24 Jun 2013 14:55:55 +0200 Subject: [PATCH] simplify settings, remove log application, add development scripts --- docbow-ctl | 12 + docbow_project/log/admin.py | 29 -- docbow_project/log/handler.py | 22 -- docbow_project/log/models.py | 14 - docbow_project/log/static/log/css/logline.css | 4 - docbow_project/plone/app_settings.py | 2 +- docbow_project/settings.py | 268 ++++++++++++++++++ docbow_project/settings/__init__.py | 0 .../{log => settings_old}/__init__.py | 0 .../{settings => settings_old}/dev.py | 0 .../{settings => settings_old}/main.py | 0 .../{settings => settings_old}/prod.py | 0 docbow_project/wsgi.py | 21 ++ load-base-data.sh | 2 + manage.py | 11 - requirements.txt | 3 +- run.sh | 14 + setup.py | 120 +++++++- start.sh | 24 ++ 19 files changed, 459 insertions(+), 87 deletions(-) create mode 100755 docbow-ctl delete mode 100644 docbow_project/log/admin.py delete mode 100644 docbow_project/log/handler.py delete mode 100644 docbow_project/log/models.py delete mode 100644 docbow_project/log/static/log/css/logline.css create mode 100644 docbow_project/settings.py delete mode 100644 docbow_project/settings/__init__.py rename docbow_project/{log => settings_old}/__init__.py (100%) rename docbow_project/{settings => settings_old}/dev.py (100%) rename docbow_project/{settings => settings_old}/main.py (100%) rename docbow_project/{settings => settings_old}/prod.py (100%) create mode 100644 docbow_project/wsgi.py create mode 100755 load-base-data.sh delete mode 100755 manage.py create mode 100755 run.sh create mode 100755 start.sh diff --git a/docbow-ctl b/docbow-ctl new file mode 100755 index 0000000..952aa1d --- /dev/null +++ b/docbow-ctl @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import sys +import os.path + +if __name__ == "__main__": + from django.core.management import execute_from_command_line + + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "docbow_project.settings") + os.environ.setdefault('DEBUG', '1') + + execute_from_command_line(sys.argv) diff --git a/docbow_project/log/admin.py b/docbow_project/log/admin.py deleted file mode 100644 index 553e223..0000000 --- a/docbow_project/log/admin.py +++ /dev/null @@ -1,29 +0,0 @@ -import django.contrib.admin as admin - -import models -from docbow_project.utils import filter_link, add_link -from ..docbow.models import DocbowUser -import docbow_project.actions as actions - -cache = {} - -class LogLineAdmin(admin.ModelAdmin): - list_display = [ 'timestamp', 'levelname', 'ip2', 'user2', 'message' ] - fields = [ 'timestamp', 'levelname', 'ip', 'user3', 'message'] - readonly_fields = fields - list_filter = [ 'name', 'levelname', 'user' ] - date_hierarchy = 'timestamp' - actions = [ actions.export_as_csv ] - search_fields = [ 'user', 'ip', 'message' ] - - class Media: - css = { - 'all': ('log/css/logline.css',), - } - - ip2 = filter_link('ip', 'Adresse IP') - user2 = add_link('user', DocbowUser, 'username', filter_link('user', 'Utilisateur')) - user3 = add_link('user', DocbowUser, 'username', metamodel=models.LogLine) - - -admin.site.register(models.LogLine, LogLineAdmin) diff --git a/docbow_project/log/handler.py b/docbow_project/log/handler.py deleted file mode 100644 index f2559ca..0000000 --- a/docbow_project/log/handler.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging -import datetime as dt - -class LogToDbHandler(logging.Handler): - models = None - - def __init__(self, *args, **kwargs): - logging.Handler.__init__(self, *args, **kwargs) - - def emit(self, record): - if not self.models: - import models - self.models = models - message = self.format(record) - log_line = self.models.LogLine(timestamp=dt.datetime.now(), - levelname=record.levelname, - name=record.name, - ip=getattr(record, 'ip', 'None'), - user=str(getattr(record, 'user', 'Anonymous')), - message=message) - log_line.save(using='log') - diff --git a/docbow_project/log/models.py b/docbow_project/log/models.py deleted file mode 100644 index a8ff381..0000000 --- a/docbow_project/log/models.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.db.models import Model, CharField, TextField, DateTimeField, IntegerField - -class LogLine(Model): - timestamp = DateTimeField() - name = CharField(max_length=128) - levelname = CharField(max_length=10) - ip = CharField(max_length=15) - user = CharField(max_length=30) - message = TextField() - - class Meta: - ordering = [ '-timestamp' ] - verbose_name = 'Trace' - app_label = 'auth' diff --git a/docbow_project/log/static/log/css/logline.css b/docbow_project/log/static/log/css/logline.css deleted file mode 100644 index 133642a..0000000 --- a/docbow_project/log/static/log/css/logline.css +++ /dev/null @@ -1,4 +0,0 @@ -.external-link { - background: url('/static/images/external-link-ltr-icon.png') center right no-repeat; - padding-right: 13px -} diff --git a/docbow_project/plone/app_settings.py b/docbow_project/plone/app_settings.py index e8bdd61..f1258a8 100644 --- a/docbow_project/plone/app_settings.py +++ b/docbow_project/plone/app_settings.py @@ -1,3 +1,3 @@ from django.conf import settings -PLONE_GED_DIRECTORY = getattr(settings, 'PLONE_GED_DIRECTORY', None) +PLONE_GED_DIRECTORY = getattr(settings, 'DOCBOW_PLONE_GED_DIRECTORY', None) diff --git a/docbow_project/settings.py b/docbow_project/settings.py new file mode 100644 index 0000000..4a51385 --- /dev/null +++ b/docbow_project/settings.py @@ -0,0 +1,268 @@ +# -*- coding: utf-8 -*- +import os +gettext_noop = lambda s: s + +PROJECT_PATH = os.path.join(os.path.dirname(__file__), '..') +PROJECT_NAME = 'docbow' + +# boolean settings +__ENVIRONMENT_DEFAULTS = dict( + # booleans + DEBUG=False, + DEBUG_PROPAGATE_EXCEPTIONS=False, + USE_DEBUG_TOOLBAR=False, + TEMPLATE_DEBUG=False, + # paths + STATIC_ROOT='/var/lib/%s/static' % PROJECT_NAME, + STATIC_URL='/static/', + MEDIA_ROOT='/var/lib/%s/media' % PROJECT_NAME, + MEDIA_URL='/media/', + ADMIN_MEDIA_PREFIX='/static/grappelli/', + # i18n/l18n + TIME_ZONE = 'Europe/Paris', + LANGUAGE_CODE = 'fr', + SITE_ID = 1, + USE_I18N=True, + USE_L10N=True, + USE_TZ=True, + # core + ROOT_URLCONF='docbow_project.urls', + TEMPLATE_DIRS=('/var/lib/%s/templates' % PROJECT_NAME,), + STATICFILES_DIRS=('/var/lib/%s/extra-static/' % PROJECT_NAME,), + GRAPPELLI_ADMIN_TITLE=u"Administration de la plate-forme d'échange sécurisée du Parlement wallon", + FILE_PER_PAGE=30, + TINYMCE_SPELLCHECKER=True, + TINYMCE_JS_URL="/static/js/tiny_mce/tiny_mce_src.js", + TINYMCE_COMPRESSOR=True, + DO_NOT_SEND_GROUPS=( + u'Administrateurs des groupes', + u'Administrateurs des types de document', + u'Administrateurs des utilisateurs', + u"Utilisateurs de l'application" ), + CONTACT_GROUPS=(u'Contact « Administrateur du système »',), + LOCALE_PATHS=(os.path.join(PROJECT_PATH, 'docbow_project', 'locale'),), + DEFAULT_FROM_EMAIL='admin@example.com', + CONTACT_SUBJECT_PREFIX='Contact depuis docbow: ', + ALLOWED_HOSTS=['*'], + LOGIN_REDIRECT_URL='/inbox', + MESSAGE_STORAGE='django.contrib.messages.storage.fallback.FallbackStorage', + AUTHENTICATION_BACKENDS=( + 'django_auth_ldap.backend.LDAPBackend', + 'docbow_project.auth_backend.DelegationAuthBackend', + 'django.contrib.auth.backends.ModelBackend' ), + AUTH_LDAP_SERVER_URI='ldaps://ldap.libre-entreprise.org:636', + AUTH_LDAP_BIND_DN='uid=admin,ou=people,o=entrouvert,ou=companies,o=libre-entreprise', + AUTH_LDAP_BIND_PASSWORD='', + INTERNAL_IPS=('127.0.0.1','82.234.244.169'), + ADMINS=(), + SOUTH_TESTS_MIGRATE=True, + DOCBOW_BASE_URL='http://localhost:8000', + OVH_SMS_ACCOUNT='sms-db26857-1', + OVH_SMS_LOGIN='pfwb', + OVH_SMS_PASSWORD='', + OVH_SMS_FROM='Dauvergne', + DOCBOW_SMS_CARRIER_CLASS='docbow_project.sms_carrier_ovh.OVHSMSCarrier', + DOCBOW_ORGANIZATION_SHORT='PFWB', + DOCBOW_ORGANIZATION=u'Parlement de la Fédération Wallonie-Bruxelles', + DOCBOW_PLONE_GED_DIRECTORY='/var/lib/%s/ged/', + DOCBOW_NOTIFIERS=( + 'docbow_project.docbow.notification.MailNotifier', + 'docbow_project.docbow.notification.SMSNotifier', + ), +) + +for key, default in __ENVIRONMENT_DEFAULTS.iteritems(): + try: + value = os.environ['DJANGO_'+key] + if default is True or default is False: + if value.isdigit(): + value = bool(int(value)) + else: + value = value.lower() in ('yes', 'true', 'y') + elif isinstance(default, (tuple, list)): + value = [ x.strip() for x in value.split(':')] + if len(default): + if isinstance(default[0], (tuple, list)) or key in ('ADMINS',): + value = [ x.split(';') for x in value ] + elif isinstance(default[0]. unicode): + value = [ unicode(x, 'utf8') for x in value ] + elif isinstance(default, unicode): + value = unicode(value, 'utf8') + except KeyError: + value = default + globals()[key] = value + +# FIXME !! to remove in the future +TEMPLATE_DIRS = (os.path.join(PROJECT_PATH, 'docbow_project', 'templates'),) + TEMPLATE_DIRS + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': os.environ.get('DATABASE_ENGINE', 'django.db.backends.sqlite3'), + 'NAME': os.environ.get('DATABASE_NAME', os.path.join(PROJECT_PATH, PROJECT_NAME + '.db')), + } +} + +# Hey Entr'ouvert is in France !! + +LANGUAGES = ( + ('fr', gettext_noop('French')), +) + +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.transaction.TransactionMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'docbow_project.docbow.middleware.KeepUserAroundMiddleware', + 'django_journal.middleware.JournalMiddleware', +) + +INSTALLED_APPS = ( + 'docbow_project.docbow', + 'docbow_project.plone', + 'grappelli', + 'django_journal', + 'uni_form', + 'tinymce', + 'south', + 'django.contrib.admin', + 'django.contrib.contenttypes', + 'django.contrib.auth', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.humanize', +) + +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +) + +TINYMCE_DEFAULT_CONFIG = { + 'theme': "advanced", +} + +DOCBOW_MENU = [ + ('send-file', gettext_noop('send-file_menu')), + ('inbox', gettext_noop('inbox_menu')), + ('outbox', gettext_noop('outbox_menu')), + ('docbow_admin:index', gettext_noop('admin_menu')), + ('profile', gettext_noop('profile_menu')), + ('auth_password_change', gettext_noop('password_change_menu')), + ('delegate', gettext_noop('delegate_menu')), + ('mailing-lists', gettext_noop('mailing-lists')), + ('help', gettext_noop('help_menu')), + ('contact', gettext_noop('contact_menu')), + ] + +import logging.handlers + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'syslog': { + 'format': PROJECT_NAME + '(pid=%(process)d) %(levelname)s %(name)s: %(message)s', + }, + 'syslog_debug': { + 'format': PROJECT_NAME + '(pid=%(process)d) %(levelname)s %(asctime)s t_%(thread)s %(name)s: %(message)s', + }, + }, + 'handlers': { + 'syslog': { + 'level': 'DEBUG', + 'class': 'entrouvert.logging.handlers.SysLogHandler', + 'formatter': 'syslog_debug' if DEBUG else 'syslog', + 'facility': logging.handlers.SysLogHandler.LOG_LOCAL0, + 'address': '/dev/log', + 'max_length': 999, + 'filters': [], + }, + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler', + 'filters': [], + 'include_html': True, + }, + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'syslog_debug', + 'level': 'DEBUG', + }, + }, + 'loggers': { + 'django.db': { + 'handlers': [ 'mail_admins', 'syslog' ], + 'level': 'INFO', + 'propagate': False, + }, + 'docbow.mail_interface': { + 'handlers': [ 'mail_admins', 'syslog' ], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + 'docbow': { + 'handlers': [ 'mail_admins', 'syslog' ], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + '': { + 'handlers': [ 'mail_admins', 'syslog' ], + 'level': 'DEBUG', + }, + }, +} + +if DEBUG: + for logger in LOGGING['loggers']: + logger['handlers'].append('console') + +# LDAP backend configuration +import ldap +from django_auth_ldap.config import LDAPSearch, GroupOfUniqueNamesType + +AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=people,o=entrouvert,ou=companies,o=libre-entreprise", + ldap.SCOPE_SUBTREE, "(uid=%(user)s)") +AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType() +AUTH_LDAP_GROUP_SEARCH = LDAPSearch("o=libre-entreprise", + ldap.SCOPE_SUBTREE, "(objectClass=legroup)") +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + "is_staff": "cn=ldapadmins,ou=groups,o=entrouvert,ou=companies,o=libre-entreprise", + "is_superuser": "cn=ldapadmins,ou=groups,o=entrouvert,ou=companies,o=libre-entreprise", +} +AUTH_LDAP_USER_ATTR_MAP = { + "first_name": "givenName", + "last_name": "sn", + "email": "mail" +} + +try: + from local_settings import * +except ImportError, e: + if 'local_settings' not in e.args[0]: + raise + +if USE_DEBUG_TOOLBAR: + try: + import debug_toolbar + MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) + INSTALLED_APPS += ('debug_toolbar',) + DEBUG_TOOLBAR_CONFIG = { 'INTERCEPT_REDIRECTS': False } + except ImportError: + print "Debug toolbar missing, not loaded" + +# syntax checks +for admin in ADMINS: + assert len(admin) == 2, 'ADMINS setting must be a colon separated list of name and emails separated by a semi-colon' + assert '@' in admin[1], 'ADMINS setting pairs second value must be emails' diff --git a/docbow_project/settings/__init__.py b/docbow_project/settings/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/docbow_project/log/__init__.py b/docbow_project/settings_old/__init__.py similarity index 100% rename from docbow_project/log/__init__.py rename to docbow_project/settings_old/__init__.py diff --git a/docbow_project/settings/dev.py b/docbow_project/settings_old/dev.py similarity index 100% rename from docbow_project/settings/dev.py rename to docbow_project/settings_old/dev.py diff --git a/docbow_project/settings/main.py b/docbow_project/settings_old/main.py similarity index 100% rename from docbow_project/settings/main.py rename to docbow_project/settings_old/main.py diff --git a/docbow_project/settings/prod.py b/docbow_project/settings_old/prod.py similarity index 100% rename from docbow_project/settings/prod.py rename to docbow_project/settings_old/prod.py diff --git a/docbow_project/wsgi.py b/docbow_project/wsgi.py new file mode 100644 index 0000000..b2804a8 --- /dev/null +++ b/docbow_project/wsgi.py @@ -0,0 +1,21 @@ +""" +WSGI config for docbow project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "docbow_project.settings") + +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() diff --git a/load-base-data.sh b/load-base-data.sh new file mode 100755 index 0000000..5997955 --- /dev/null +++ b/load-base-data.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./run.sh loaddata filetype content groups diff --git a/manage.py b/manage.py deleted file mode 100755 index 026e470..0000000 --- a/manage.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -import sys -import os.path - -from django.core.management import execute_manager -import docbow_project.settings.dev as settings - -sys.path.insert(0, os.path.dirname(__file__)) - -if __name__ == "__main__": - execute_manager(settings) diff --git a/requirements.txt b/requirements.txt index ba28897..45e2fa2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,11 +8,10 @@ django-auth-ldap<1.1 python-ldap<3.0.0 BeautifulSoup<3.3.0 M2Crypto<1.0.0 -wsgiref<1.0.0 pyasn1<1.0.0 pyasn1-modules<1.0.0 rfc3161==0.1.6 -psycopg2<2.5.0 gunicorn django_journal<2.0.0 django-picklefield==0.3.0 +git+git://repos.entrouvert.org/python-entrouvert.git/#egg=entrouvert-1.1.9999 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..86bb6bc --- /dev/null +++ b/run.sh @@ -0,0 +1,14 @@ +#!/bin/sh +PROJECT=docbow +CTL=${PROJECT}-ctl +VENV=${PROJECT}-venv + +if [ "$VIRTUAL_ENV" != "" ]; then + deactivate +fi +if [ ! -d $VENV ]; then + ./start.sh +else + . ./$VENV/bin/activate + ./$CTL "${@:-runserver}" +fi diff --git a/setup.py b/setup.py index b750307..4dbe634 100755 --- a/setup.py +++ b/setup.py @@ -1,13 +1,125 @@ -#!/usr/bin/python -from distutils.core import setup +#!env python + +from setuptools import setup, find_packages +from setuptools.command.install_lib import install_lib as _install_lib +from distutils.command.build import build as _build +from distutils.command.sdist import sdist as _sdist +from distutils.cmd import Command + +class compile_translations(Command): + description = 'compile message catalogs to MO files via django compilemessages' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + import os + import sys + from django.core.management.commands.compilemessages import \ + compile_messages + for path in ['docbow_project']: + if not os.path.exists(os.path.join(path, 'locale')): + continue + curdir = os.getcwd() + os.chdir(os.path.realpath(path)) + compile_messages(stderr=sys.stderr) + os.chdir(curdir) + +class build(_build): + sub_commands = [('compile_translations', None)] + _build.sub_commands + +class sdist(_sdist): + sub_commands = [('compile_translations', None)] + _sdist.sub_commands + +class install_lib(_install_lib): + def run(self): + self.run_command('compile_translations') + _install_lib.run(self) + + +def get_version(): + import glob + import re + import os + + version = None + for d in glob.glob('*'): + if not os.path.isdir(d): + continue + module_file = os.path.join(d, '__init__.py') + if not os.path.exists(module_file): + continue + for v in re.findall("""__version__ *= *['"](.*)['"]""", + open(module_file).read()): + assert version is None + version = v + if version: + break + assert version is not None + if os.path.exists('.git'): + import subprocess + p = subprocess.Popen(['git','describe','--dirty','--match=v*'], + stdout=subprocess.PIPE) + result = p.communicate()[0] + assert p.returncode == 0, 'git returned non-zero' + new_version = result.split()[0][1:] + assert not new_version.endswith('-dirty'), 'git workdir is not clean' + assert new_version.split('-')[0] == version, '__version__ must match the last git annotated tag' + version = new_version.replace('-', '.') + return version setup(name='docbow', version='1.0', license='AGPL 3.0', - description='Document box for the Wallon Parliement', + description='Document box for the Wallon Parliament', url='https://dev.entrouvert.org/projects/docbow-pub/', author="Entr'ouvert", author_email='info@entrouvert.com', maintainer='Benjamin Dauvergne', maintainer_email='bdauvergne@entrouvert.com', - packages=['docbow_project']) + scripts=('docbow-ctl',), + package_data={ + '': [ + 'templates/**.html', + 'templates/**.txt', + 'static/**.png', + 'static/**.gif', + 'static/**.css', + 'static/**.js', + 'locale/**.mo', + 'fixtures/*.json', + ] + }, + packages=find_packages(), + install_requires=[ + 'Django==1.5' + 'South<0.8.0' + 'django-debug-toolbar<0.9.0' + 'django-grappelli<2.5.0' + 'django-tinymce<1.6.0' + 'django-uni-form<0.9.0' + 'django-auth-ldap<1.1' + 'python-ldap<3.0.0' + 'BeautifulSoup<3.3.0' + 'M2Crypto<1.0.0' + 'pyasn1<1.0.0' + 'pyasn1-modules<1.0.0' + 'rfc3161==0.1.6' + 'gunicorn' + 'django_journal<2.0.0' + 'django-picklefield==0.3.0' + 'entrouvert<2.0', + ], + setup_requires=[ + 'Django==1.5', + ], + dependency_links = [ + 'git+git://repos.entrouvert.org/python-entrouvert.git/#egg=entrouvert-1.1.9999', + ], + cmdclass={'build': build, 'install_lib': install_lib, + 'compile_translations': compile_translations, + 'sdist': sdist}) diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..e365230 --- /dev/null +++ b/start.sh @@ -0,0 +1,24 @@ +#!/bin/sh +PROJECT=docbow +CTL=${PROJECT}-ctl +VENV=${PROJECT}-venv + +if [ "$VIRTUAL_ENV" = "" ]; then + if which mkvirtualenv >/dev/null 2>&1; then + workon $PROJECT || (mkvirtualenv $PROJECT; workon $PROJECT) + else + if [ ! -d $VENV ]; then + virtualenv --system-site-packages $VENV 2>/dev/null || virtualenv $VENV + fi + . ./$VENV/bin/activate + fi +fi +easy_install -U pip distribute +pip install -U 'django==1.5' +pip install -U -r requirements.txt +if [ ! -f $PROJECT.db ]; then + ./$CTL syncdb --all --noinput + ./$CTL migrate --fake + ./load-base-data.sh +fi +./$CTL runserver