OpenSlides/openslides/__main__.py

367 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.__main__
~~~~~~~~~~~~~~~~~~~
Main script for OpenSlides
:copyright: 20112013 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
import argparse
import os
import shutil
import sys
from django.conf import ENVIRONMENT_VARIABLE
from django.core.management import execute_from_command_line
from openslides import get_version
from openslides.utils.main import (
detect_openslides_type,
ensure_settings,
filesystem2unicode,
get_browser_url,
get_database_path_from_settings,
get_default_settings_path,
get_default_user_data_path,
get_port,
get_user_data_path_values_with_path,
setup_django_settings_module,
start_browser,
write_settings)
from openslides.utils.tornado_webserver import run_tornado
def main():
"""
Main entrance to OpenSlides.
"""
# Parse all command line args.
args = parse_args()
# Setup settings path: Take it either from command line or get default path
if hasattr(args, 'settings') and args.settings:
settings = args.settings
setup_django_settings_module(settings)
else:
if ENVIRONMENT_VARIABLE not in os.environ:
openslides_type = detect_openslides_type()
settings = get_default_settings_path(openslides_type)
setup_django_settings_module(settings)
else:
# The environment variable is set, so we do not need to process
# anything more here.
settings = None
# Process the subcommand's callback
return args.callback(settings, args)
def parse_args():
"""
Parses all command line arguments. The subcommand 'django' links to
Django's command-line utility.
"""
if len(sys.argv) == 1:
sys.argv.append('start')
# Init parser
description = 'Start script for OpenSlides.'
if 'manage.py' not in sys.argv[0]:
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 tornado webserver, launch the '
'default web browser and open the webinterface.')
parser = argparse.ArgumentParser(description=description)
# Add version argument
parser.add_argument(
'--version',
action='version',
version=get_version(),
help='Show version number and exit.')
# Init subparsers
subparsers = parser.add_subparsers(
dest='subcommand',
title='Available subcommands',
description="Type 'python %s <subcommand> --help' for help on a "
"specific subcommand." % parser.prog,
help='You can choose only one subcommand at once.')
# Subcommand start
subcommand_start = subparsers.add_parser(
'start',
help='Setup settings and database, start tornado webserver, launch the '
'default web browser and open the webinterface.')
add_general_arguments(subcommand_start, ('settings', 'user_data_path', 'address', 'port'))
subcommand_start.add_argument(
'--no-browser',
action='store_true',
help='Do not launch the default web browser.')
subcommand_start.set_defaults(callback=start)
# Subcommand runserver
subcommand_runserver = subparsers.add_parser(
'runserver',
help='Run OpenSlides using tornado webserver.')
add_general_arguments(subcommand_runserver, ('settings', 'user_data_path', 'address', 'port'))
subcommand_runserver.add_argument(
'--start-browser',
action='store_true',
help='Launch the default web browser and open the webinterface.')
subcommand_runserver.add_argument(
'--no-reload',
action='store_true',
help='Do not reload the webserver if source code changes.')
subcommand_runserver.set_defaults(callback=runserver)
# Subcommand syncdb
subcommand_syncdb = subparsers.add_parser(
'syncdb',
help='Create or update database tables.')
add_general_arguments(subcommand_syncdb, ('settings', 'user_data_path'))
subcommand_syncdb.set_defaults(callback=syncdb)
# Subcommand createsuperuser
subcommand_createsuperuser = subparsers.add_parser(
'createsuperuser',
help="Make sure the user 'admin' exists and uses 'admin' as password.")
add_general_arguments(subcommand_createsuperuser, ('settings', 'user_data_path'))
subcommand_createsuperuser.set_defaults(callback=createsuperuser)
# Subcommand backupdb
subcommand_backupdb = subparsers.add_parser(
'backupdb',
help='Store a backup copy of the SQLite3 database at the given path.')
add_general_arguments(subcommand_backupdb, ('settings', 'user_data_path'))
subcommand_backupdb.add_argument(
'path',
help='Path to the backup file. An existing file will be overwritten.')
subcommand_backupdb.set_defaults(callback=backupdb)
# Subcommand deletedb
subcommand_deletedb = subparsers.add_parser(
'deletedb',
help='Delete the SQLite3 database.')
add_general_arguments(subcommand_deletedb, ('settings', 'user_data_path'))
subcommand_deletedb.set_defaults(callback=deletedb)
# Subcommand create-dev-settings
subcommand_create_dev_settings = subparsers.add_parser(
'create-dev-settings',
help='Create a settings file at current working directory for development use.')
subcommand_create_dev_settings.set_defaults(callback=create_dev_settings)
# Subcommand django
subcommand_django_command_line_utility = subparsers.add_parser(
'django',
description="Link to Django's command-line utility. Type "
"'python %s django help' for more help on this." % parser.prog,
help="Call Django's command-line utility.")
subcommand_django_command_line_utility.set_defaults(
callback=django_command_line_utility,
django_args=['%s' % subcommand_django_command_line_utility.prog])
known_args, unknown_args = parser.parse_known_args()
if known_args.subcommand == 'django':
if not unknown_args:
unknown_args.append('help')
known_args.django_args.extend(unknown_args)
else:
if unknown_args:
parser.error('Unknown arguments %s found.' % ' '.join(unknown_args))
return known_args
def add_general_arguments(subcommand, arguments):
"""
Adds the named arguments to the subcommand.
"""
general_arguments = {}
openslides_type = detect_openslides_type()
general_arguments['settings'] = (
('-s', '--settings'),
dict(help="Path to settings file. If this isn't provided, the "
"%s environment variable will be used. "
"If this isn't provided too, a default path according to the "
"OpenSlides type will be used. At the moment it is %s" % (
ENVIRONMENT_VARIABLE,
get_default_settings_path(openslides_type))))
general_arguments['user_data_path'] = (
('-d', '--user-data-path'),
dict(help='Path to the directory for user specific data files like SQLite3 '
'database, uploaded media and search index. This is only used, '
'when a new settings file is created. The given path is only '
'written into the new settings file. Default according to the '
'OpenSlides is at the moment %s' % get_default_user_data_path(openslides_type)))
general_arguments['address'] = (
('-a', '--address',),
dict(default='0.0.0.0', help='IP address to listen on. Default is %(default)s.'))
general_arguments['port'] = (
('-p', '--port'),
dict(type=int,
default=80,
help='Port to listen on. Default as admin or root is %(default)d, else 8000.'))
for argument in arguments:
try:
args, kwargs = general_arguments[argument]
except KeyError:
raise TypeError('The argument %s is not a valid general argument.' % argument)
subcommand.add_argument(*args, **kwargs)
def start(settings, args):
"""
Starts OpenSlides: Runs syncdb and runs runserver (tornado webserver).
"""
ensure_settings(settings, args)
syncdb(settings, args)
args.start_browser = not args.no_browser
args.no_reload = False
runserver(settings, args)
def runserver(settings, args):
"""
Runs tornado webserver. Runs the function start_browser if the respective
argument is given.
"""
ensure_settings(settings, args)
port = get_port(address=args.address, port=args.port)
if args.start_browser:
browser_url = get_browser_url(address=args.address, port=port)
start_browser(browser_url)
run_tornado(args.address, port, not args.no_reload)
def syncdb(settings, args):
"""
Run syncdb to create or update the database.
"""
ensure_settings(settings, args)
# TODO: Check use of filesystem2unicode here.
path = filesystem2unicode(os.path.dirname(get_database_path_from_settings()))
if not os.path.exists(path):
os.makedirs(path)
execute_from_command_line(["", "syncdb", "--noinput"])
return 0
def createsuperuser(settings, args):
"""
Creates or resets the admin user. Returns 0 to show success.
"""
ensure_settings(settings, args)
# can't be imported in global scope as it already requires
# the settings module during import
from openslides.participant.api import create_or_reset_admin_user
if create_or_reset_admin_user():
print('Admin user successfully created.')
else:
print('Admin user successfully reset.')
return 0
def backupdb(settings, args):
"""
Stores a backup copy of the SQlite3 database. Returns 0 on success, else 1.
"""
ensure_settings(settings, args)
from django.db import connection, transaction
@transaction.commit_manually
def do_backup(src_path, dest_path):
# perform a simple file-copy backup of the database
# first we need a shared lock on the database, issuing a select()
# will do this for us
cursor = connection.cursor()
cursor.execute("SELECT count(*) from sqlite_master")
# now copy the file
try:
shutil.copy(src_path, dest_path)
except IOError:
raise Exception("Database backup failed.")
# and release the lock again
transaction.commit()
database_path = get_database_path_from_settings()
if database_path:
do_backup(database_path, args.path)
print('Database %s successfully stored at %s.' % (database_path, args.path))
return_value = 0
else:
print('Error: Default database is not SQLite3. Only SQLite3 databases '
'can currently be backuped.')
return_value = 1
return return_value
def deletedb(settings, args):
"""
Deletes the sqlite3 database. Returns 0 on success, else 1.
"""
ensure_settings(settings, args)
database_path = get_database_path_from_settings()
if database_path and os.path.exists(database_path):
os.remove(database_path)
print('SQLite3 database file %s successfully deleted.' % database_path)
return_value = 0
else:
print('SQLite3 database file %s does not exist.' % database_path)
return_value = 1
return return_value
def create_dev_settings(settings, args):
"""
Creates a settings file at the currect working directory for development use.
"""
settings = os.path.join(os.getcwd(), 'settings.py')
if not os.path.exists(settings):
context = get_user_data_path_values_with_path(os.getcwd())
context['debug'] = 'True'
write_settings(settings, **context)
print('Settings file at %s successfully created.' % settings)
return_value = 0
else:
print('Error: Settings file %s already exists.' % settings)
return_value = 1
return return_value
def django_command_line_utility(settings, args):
"""
Runs Django's command line utility. Returns 0 on success, else 1.
"""
if 'runserver' in args.django_args:
command = 'runserver'
elif 'syncdb' in args.django_args:
command = 'syncdb'
elif 'createsuperuser' in args.django_args:
command = 'createsuperuser'
else:
command = None
if command:
print("Error: The command '%s' is disabled in OpenSlides for use via Django's "
"command line utility." % command)
return_value = 1
else:
ensure_settings(settings, args)
execute_from_command_line(args.django_args)
return_value = 0
return return_value
if __name__ == "__main__":
exit(main())