2013-09-08 14:30:26 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
import os
|
2017-02-12 12:42:20 +01:00
|
|
|
import subprocess
|
2013-09-08 14:30:26 +02:00
|
|
|
import sys
|
2017-08-24 12:26:55 +02:00
|
|
|
from typing import Dict # noqa
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2017-02-12 12:42:20 +01:00
|
|
|
import django
|
|
|
|
from django.core.management import call_command, execute_from_command_line
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2018-01-16 16:02:23 +01:00
|
|
|
import openslides
|
2013-09-08 14:30:26 +02:00
|
|
|
from openslides.utils.main import (
|
2015-01-16 14:18:34 +01:00
|
|
|
ExceptionArgumentParser,
|
2015-06-16 10:37:23 +02:00
|
|
|
UnknownCommand,
|
2018-01-16 16:02:23 +01:00
|
|
|
get_default_settings_dir,
|
2017-02-12 12:42:20 +01:00
|
|
|
get_geiss_path,
|
2018-01-16 16:02:23 +01:00
|
|
|
get_local_settings_dir,
|
2016-02-27 18:27:03 +01:00
|
|
|
is_local_installation,
|
2017-02-12 12:42:20 +01:00
|
|
|
open_browser,
|
2015-06-16 10:37:23 +02:00
|
|
|
setup_django_settings_module,
|
|
|
|
write_settings,
|
|
|
|
)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
"""
|
|
|
|
Main entrance to OpenSlides.
|
|
|
|
"""
|
2015-01-16 14:18:34 +01:00
|
|
|
parser = get_parser()
|
|
|
|
try:
|
|
|
|
known_args, unknown_args = parser.parse_known_args()
|
|
|
|
except UnknownCommand:
|
|
|
|
unknown_command = True
|
2013-10-13 17:17:56 +02:00
|
|
|
else:
|
2015-01-16 14:18:34 +01:00
|
|
|
unknown_command = False
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
if unknown_command:
|
|
|
|
# Run a command, that is defined by the django management api
|
2016-02-27 18:27:03 +01:00
|
|
|
local_installation = is_local_installation()
|
|
|
|
setup_django_settings_module(local_installation=local_installation)
|
2015-01-16 14:18:34 +01:00
|
|
|
execute_from_command_line(sys.argv)
|
|
|
|
else:
|
2017-01-14 10:48:41 +01:00
|
|
|
# Check for unknown_args.
|
|
|
|
if unknown_args:
|
|
|
|
parser.error('Unknown arguments {}'.format(' '.join(unknown_args)))
|
2018-01-16 16:02:23 +01:00
|
|
|
|
|
|
|
# Save arguments, if one wants to access them later.
|
|
|
|
openslides.args = known_args
|
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
# Run a command that is defined here
|
|
|
|
# These are commands that can not rely on an existing settings
|
|
|
|
known_args.callback(known_args)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
def get_parser():
|
2013-09-08 14:30:26 +02:00
|
|
|
"""
|
2015-01-16 14:18:34 +01:00
|
|
|
Parses all command line arguments.
|
2013-09-08 14:30:26 +02:00
|
|
|
"""
|
2016-10-26 14:54:01 +02:00
|
|
|
if len(sys.argv) == 1:
|
|
|
|
# Use start subcommand if called by openslides console script without
|
|
|
|
# any other arguments.
|
2013-09-08 14:30:26 +02:00
|
|
|
sys.argv.append('start')
|
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
# Init parser
|
2013-09-08 14:30:26 +02:00
|
|
|
description = 'Start script for OpenSlides.'
|
|
|
|
if 'manage.py' not in sys.argv[0]:
|
2016-02-24 00:42:45 +01:00
|
|
|
description += """
|
|
|
|
If it is called without any argument, this will be treated as
|
|
|
|
if it is called with the 'start' subcommand. That means
|
|
|
|
OpenSlides will setup default settings and database, start the
|
2016-10-26 14:54:01 +02:00
|
|
|
webserver, launch the default web browser and open the
|
2016-02-24 00:42:45 +01:00
|
|
|
webinterface.
|
|
|
|
"""
|
|
|
|
epilog = """
|
|
|
|
There are some more subcommands available. They belong to Django's
|
|
|
|
command-line utility for administrative tasks. Type '%(prog)s help'
|
|
|
|
(without the two hyphen-minus characters) to list them all. Type
|
|
|
|
'%(prog)s help <subcommand>' for help on a specific subcommand.
|
|
|
|
"""
|
|
|
|
parser = ExceptionArgumentParser(
|
|
|
|
description=description,
|
|
|
|
epilog=epilog)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
# Add version argument
|
2013-09-08 14:30:26 +02:00
|
|
|
parser.add_argument(
|
|
|
|
'--version',
|
|
|
|
action='version',
|
2018-01-16 16:02:23 +01:00
|
|
|
version=openslides.__version__,
|
2013-09-08 14:30:26 +02:00
|
|
|
help='Show version number and exit.')
|
|
|
|
|
2013-10-13 17:17:56 +02:00
|
|
|
# Init subparsers
|
2013-09-08 14:30:26 +02:00
|
|
|
subparsers = parser.add_subparsers(
|
|
|
|
dest='subcommand',
|
|
|
|
title='Available subcommands',
|
2013-11-23 18:49:51 +01:00
|
|
|
description="Type '%s <subcommand> --help' for help on a "
|
2017-08-24 12:26:55 +02:00
|
|
|
"specific subcommand." % parser.prog, # type: ignore
|
2016-02-24 00:42:45 +01:00
|
|
|
help='You can choose only one subcommand at once.',
|
|
|
|
metavar='')
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
# Subcommand start
|
2016-02-24 00:42:45 +01:00
|
|
|
start_help = (
|
2016-10-26 14:54:01 +02:00
|
|
|
'Setup settings and database, start webserver, launch the '
|
2016-02-24 00:42:45 +01:00
|
|
|
'default web browser and open the webinterface. The environment '
|
|
|
|
'variable DJANGO_SETTINGS_MODULE is ignored.')
|
2013-09-08 14:30:26 +02:00
|
|
|
subcommand_start = subparsers.add_parser(
|
|
|
|
'start',
|
2016-02-24 00:42:45 +01:00
|
|
|
description=start_help,
|
|
|
|
help=start_help)
|
2017-02-15 13:22:08 +01:00
|
|
|
subcommand_start.set_defaults(callback=start)
|
2013-10-13 17:17:56 +02:00
|
|
|
subcommand_start.add_argument(
|
|
|
|
'--no-browser',
|
|
|
|
action='store_true',
|
|
|
|
help='Do not launch the default web browser.')
|
2018-01-12 08:40:15 +01:00
|
|
|
subcommand_start.add_argument(
|
|
|
|
'--debug-email',
|
|
|
|
action='store_true',
|
|
|
|
help='Change the email backend to console output.')
|
2016-03-20 22:33:03 +01:00
|
|
|
subcommand_start.add_argument(
|
|
|
|
'--host',
|
|
|
|
action='store',
|
|
|
|
default='0.0.0.0',
|
|
|
|
help='IP address to listen on. Default is 0.0.0.0.')
|
|
|
|
subcommand_start.add_argument(
|
|
|
|
'--port',
|
|
|
|
action='store',
|
|
|
|
default='8000',
|
|
|
|
help='Port to listen on. Default is 8000.')
|
2015-01-16 14:18:34 +01:00
|
|
|
subcommand_start.add_argument(
|
2018-01-16 16:02:23 +01:00
|
|
|
'--settings_dir',
|
2015-01-16 14:18:34 +01:00
|
|
|
action='store',
|
|
|
|
default=None,
|
2018-01-16 16:02:23 +01:00
|
|
|
help='The settings directory.')
|
|
|
|
subcommand_start.add_argument(
|
|
|
|
'--settings_filename',
|
|
|
|
action='store',
|
|
|
|
default='settings.py',
|
|
|
|
help='The used settings file name. The file is created, if it does not exist.')
|
2015-01-16 14:18:34 +01:00
|
|
|
subcommand_start.add_argument(
|
2016-02-27 18:27:03 +01:00
|
|
|
'--local-installation',
|
2013-09-08 14:30:26 +02:00
|
|
|
action='store_true',
|
2016-02-27 18:27:03 +01:00
|
|
|
help='Store settings and user files in a local directory.')
|
2017-02-15 13:22:08 +01:00
|
|
|
subcommand_start.add_argument(
|
|
|
|
'--use-geiss',
|
|
|
|
action='store_true',
|
|
|
|
help='Use Geiss instead of Daphne as ASGI protocol server.')
|
2015-01-16 14:18:34 +01:00
|
|
|
|
|
|
|
# Subcommand createsettings
|
2016-02-24 00:42:45 +01:00
|
|
|
createsettings_help = 'Creates the settings file.'
|
2015-01-16 14:18:34 +01:00
|
|
|
subcommand_createsettings = subparsers.add_parser(
|
|
|
|
'createsettings',
|
2016-02-24 00:42:45 +01:00
|
|
|
description=createsettings_help,
|
|
|
|
help=createsettings_help)
|
2015-01-16 14:18:34 +01:00
|
|
|
subcommand_createsettings.set_defaults(callback=createsettings)
|
|
|
|
subcommand_createsettings.add_argument(
|
2018-01-16 16:02:23 +01:00
|
|
|
'--settings_dir',
|
2015-01-16 14:18:34 +01:00
|
|
|
action='store',
|
|
|
|
default=None,
|
2018-01-16 16:02:23 +01:00
|
|
|
help='The used settings file directory. All settings files are created, even if they exist.')
|
2015-01-16 14:18:34 +01:00
|
|
|
subcommand_createsettings.add_argument(
|
2016-02-27 18:27:03 +01:00
|
|
|
'--local-installation',
|
2015-01-16 14:18:34 +01:00
|
|
|
action='store_true',
|
2016-02-27 18:27:03 +01:00
|
|
|
help='Store settings and user files in a local directory.')
|
2016-02-24 00:42:45 +01:00
|
|
|
|
|
|
|
# Help text for several Django subcommands
|
|
|
|
django_subcommands = (
|
|
|
|
('backupdb', 'Backups the SQLite3 database.'),
|
|
|
|
('createsuperuser', 'Creates or resets the admin user.'),
|
|
|
|
('migrate', 'Updates database schema.'),
|
|
|
|
('runserver', 'Starts the Tornado webserver.'),
|
|
|
|
)
|
|
|
|
for django_subcommand, help_text in django_subcommands:
|
2017-08-24 12:26:55 +02:00
|
|
|
subparsers._choices_actions.append( # type: ignore
|
|
|
|
subparsers._ChoicesPseudoAction( # type: ignore
|
2016-02-24 00:42:45 +01:00
|
|
|
django_subcommand,
|
|
|
|
(),
|
|
|
|
help_text))
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
return parser
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
def start(args):
|
2013-09-08 14:30:26 +02:00
|
|
|
"""
|
2015-01-16 14:18:34 +01:00
|
|
|
Starts OpenSlides: Runs migrations and runs runserver.
|
2013-09-08 14:30:26 +02:00
|
|
|
"""
|
2018-01-16 16:02:23 +01:00
|
|
|
settings_dir = args.settings_dir
|
|
|
|
settings_filename = args.settings_filename
|
2016-02-27 18:27:03 +01:00
|
|
|
local_installation = is_local_installation()
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2018-01-16 16:02:23 +01:00
|
|
|
if settings_dir is None:
|
2016-02-27 18:27:03 +01:00
|
|
|
if local_installation:
|
2018-01-16 16:02:23 +01:00
|
|
|
settings_dir = get_local_settings_dir()
|
2015-01-16 14:18:34 +01:00
|
|
|
else:
|
2018-01-16 16:02:23 +01:00
|
|
|
settings_dir = get_default_settings_dir()
|
2013-10-28 16:34:53 +01:00
|
|
|
|
2018-01-16 16:02:23 +01:00
|
|
|
# Write django settings if it does not exists.
|
|
|
|
settings_path = os.path.join(settings_dir, settings_filename)
|
2015-01-16 14:18:34 +01:00
|
|
|
if not os.path.isfile(settings_path):
|
|
|
|
createsettings(args)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
# Set the django setting module and run migrations
|
|
|
|
# A manual given environment variable will be overwritten
|
2016-02-27 18:27:03 +01:00
|
|
|
setup_django_settings_module(settings_path, local_installation=local_installation)
|
2017-02-12 12:42:20 +01:00
|
|
|
django.setup()
|
|
|
|
from django.conf import settings
|
|
|
|
|
2018-01-12 08:40:15 +01:00
|
|
|
if args.debug_email:
|
|
|
|
settings.EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
|
|
|
2017-02-15 13:22:08 +01:00
|
|
|
# Migrate database
|
2017-02-12 12:42:20 +01:00
|
|
|
call_command('migrate')
|
|
|
|
|
2017-02-15 13:22:08 +01:00
|
|
|
if args.use_geiss:
|
|
|
|
# Make sure Redis is used.
|
2017-02-12 12:42:20 +01:00
|
|
|
if settings.CHANNEL_LAYERS['default']['BACKEND'] != 'asgi_redis.RedisChannelLayer':
|
2017-02-15 13:22:08 +01:00
|
|
|
raise RuntimeError("You have to use the ASGI Redis backend in the settings to use Geiss.")
|
2017-02-12 12:42:20 +01:00
|
|
|
|
|
|
|
# Download Geiss and collect the static files.
|
|
|
|
call_command('getgeiss')
|
|
|
|
call_command('collectstatic', interactive=False)
|
|
|
|
|
|
|
|
# Open the browser
|
|
|
|
if not args.no_browser:
|
|
|
|
open_browser(args.host, args.port)
|
|
|
|
|
|
|
|
# Start Geiss in its own thread
|
|
|
|
subprocess.Popen([
|
|
|
|
get_geiss_path(),
|
|
|
|
'--host', args.host,
|
|
|
|
'--port', args.port,
|
|
|
|
'--static', '/static/:{}'.format(settings.STATIC_ROOT),
|
|
|
|
'--static', '/media/:{}'.format(settings.MEDIA_ROOT),
|
|
|
|
])
|
|
|
|
|
2017-02-15 13:22:08 +01:00
|
|
|
# Start one worker in this thread. There can be only one worker as
|
|
|
|
# long as SQLite3 is used.
|
2017-02-12 12:42:20 +01:00
|
|
|
call_command('runworker')
|
2013-09-08 14:30:26 +02:00
|
|
|
|
2017-02-12 12:42:20 +01:00
|
|
|
else:
|
|
|
|
# Open the browser
|
|
|
|
if not args.no_browser:
|
|
|
|
open_browser(args.host, args.port)
|
|
|
|
|
2017-02-15 13:22:08 +01:00
|
|
|
# Start Daphne and one worker
|
|
|
|
#
|
2017-02-12 12:42:20 +01:00
|
|
|
# Use flag --noreload to tell Django not to reload the server.
|
2017-02-15 13:22:08 +01:00
|
|
|
# Therefor we have to set the keyword noreload to False because Django
|
|
|
|
# parses this directly to the use_reloader keyword.
|
|
|
|
#
|
2017-02-12 12:42:20 +01:00
|
|
|
# Use flag --insecure to serve static files even if DEBUG is False.
|
2017-02-15 13:22:08 +01:00
|
|
|
#
|
|
|
|
# Use flag --nothreading to tell Django Channels to run in single
|
|
|
|
# thread mode with one worker only. Therefor we have to set the keyword
|
|
|
|
# nothreading to False because Django parses this directly to
|
|
|
|
# use_threading keyword.
|
2017-02-12 12:42:20 +01:00
|
|
|
call_command(
|
|
|
|
'runserver',
|
|
|
|
'{}:{}'.format(args.host, args.port),
|
2017-02-15 13:22:08 +01:00
|
|
|
noreload=False, # Means True, see above.
|
2017-02-12 12:42:20 +01:00
|
|
|
insecure=True,
|
2017-02-15 13:22:08 +01:00
|
|
|
nothreading=False, # Means True, see above.
|
2017-02-12 12:42:20 +01:00
|
|
|
)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
2015-01-16 14:18:34 +01:00
|
|
|
def createsettings(args):
|
2013-10-28 16:34:53 +01:00
|
|
|
"""
|
2015-01-16 14:18:34 +01:00
|
|
|
Creates settings for OpenSlides.
|
2013-10-28 16:34:53 +01:00
|
|
|
"""
|
2018-01-16 16:02:23 +01:00
|
|
|
settings_dir = args.settings_dir
|
2016-02-27 18:27:03 +01:00
|
|
|
local_installation = is_local_installation()
|
2017-08-24 12:26:55 +02:00
|
|
|
context = {} # type: Dict[str, str]
|
2013-10-28 16:34:53 +01:00
|
|
|
|
2016-02-27 18:27:03 +01:00
|
|
|
if local_installation:
|
2018-01-16 16:02:23 +01:00
|
|
|
if settings_dir is None:
|
|
|
|
settings_dir = get_local_settings_dir()
|
2015-01-16 14:18:34 +01:00
|
|
|
context = {
|
2018-01-16 16:02:23 +01:00
|
|
|
'openslides_user_data_dir': repr(os.path.join(os.getcwd(), 'personal_data', 'var')),
|
2015-01-16 14:18:34 +01:00
|
|
|
'debug': 'True'}
|
2013-10-28 16:34:53 +01:00
|
|
|
|
2018-01-16 16:02:23 +01:00
|
|
|
settings_path = write_settings(settings_dir, args.settings_filename, **context)
|
2015-01-16 14:18:34 +01:00
|
|
|
print('Settings created at %s' % settings_path)
|
2013-09-08 14:30:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2016-04-07 00:43:53 +02:00
|
|
|
sys.exit(main())
|