OpenSlides/openslides/main.py

405 lines
11 KiB
Python
Raw Normal View History

2012-04-25 22:29:19 +02:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.main
~~~~~~~~~~~~~~~
Main script to start and set up OpenSlides.
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
import base64
import ctypes
import optparse
import os
import socket
import sys
import tempfile
import threading
import time
import webbrowser
from tornado.options import options, define, parse_command_line
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.wsgi
import django.core.handlers.wsgi
import django.conf
from django.core.management import execute_from_command_line
from openslides import get_version
2012-08-23 23:34:30 +02:00
CONFIG_TEMPLATE = """#!/usr/bin/env python
# -*- coding: utf-8 -*-
import openslides.main
2012-11-01 10:56:38 +01:00
from openslides.global_settings import *
2012-08-05 09:52:00 +02:00
# Use 'DEBUG = True' to get more details for server errors
# (Default for releases: 'False')
DEBUG = False
TEMPLATE_DEBUG = DEBUG
DBPATH = %(dbpath)s
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': DBPATH,
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
}
}
# Set timezone
TIME_ZONE = 'Europe/Berlin'
# Make this unique, and don't share it with anybody.
SECRET_KEY = %(default_key)r
# Add OpenSlides plugins to this list (see example entry in comment)
INSTALLED_PLUGINS = (
# 'pluginname',
)
INSTALLED_APPS += INSTALLED_PLUGINS
"""
KEY_LENGTH = 30
# sentinel used to signal that the database ought to be stored
# relative to the portable's directory
_portable_db_path = object()
2012-08-14 14:19:59 +02:00
def process_options(argv=None, check_args=True):
if argv is None:
argv = sys.argv[1:]
parser = optparse.OptionParser(
2013-01-23 19:37:12 +01:00
description="Run openslides using the tornado webserver")
parser.add_option("-a", "--address", help="IP Address to listen on.")
parser.add_option("-p", "--port", type="int", help="Port to listen on.")
2012-08-14 14:19:59 +02:00
parser.add_option(
"--syncdb", action="store_true",
help="Update/create database before starting the server.")
2012-08-14 14:19:59 +02:00
parser.add_option(
"--reset-admin", action="store_true",
help="Make sure the user 'admin' exists and uses 'admin' as password.")
2012-08-14 14:19:59 +02:00
parser.add_option(
"-s", "--settings", help="Path to the openslides configuration.")
parser.add_option(
"--no-reload", action="store_true",
help="Do not reload the development server.")
parser.add_option(
"--version", action="store_true",
help="Show version and exit.")
opts, args = parser.parse_args(argv)
if opts.version:
print get_version()
exit(0)
# Don't check for further args if we come from our custom management
# command, that always sets them
if args and check_args:
2012-08-14 14:19:59 +02:00
sys.stderr.write("This command does not take arguments!\n\n")
parser.print_help()
sys.exit(1)
return opts
2012-11-24 20:35:06 +01:00
def main(argv=None, check_args=True):
opts = process_options(argv, check_args)
_main(opts)
2012-11-24 20:35:06 +01:00
def win32_portable_main(argv=None):
"""special entry point for the win32 portable version"""
opts = process_options(argv)
database_path = None
if opts.settings is None:
portable_dir = get_portable_path()
try:
fd, test_file = tempfile.mkstemp(dir=portable_dir)
except OSError:
portable_dir_writeable = False
else:
portable_dir_writeable = True
os.close(fd)
os.unlink(test_file)
if portable_dir_writeable:
2012-11-24 20:35:06 +01:00
opts.settings = os.path.join(
portable_dir, "openslides", "settings.py")
database_path = _portable_db_path
_main(opts, database_path=database_path)
2012-11-24 20:35:06 +01:00
def _main(opts, database_path=None):
2012-08-14 14:19:59 +02:00
# Find the path to the settings
2012-11-01 10:56:38 +01:00
settings_path = opts.settings
if settings_path is None:
settings_path = get_user_config_path('openslides', 'settings.py')
2012-08-14 14:19:59 +02:00
# Create settings if necessary
2012-11-01 10:56:38 +01:00
if not os.path.exists(settings_path):
create_settings(settings_path, database_path)
2012-08-14 14:19:59 +02:00
# Set the django environment to the settings
2012-11-01 10:56:38 +01:00
setup_django_environment(settings_path)
2012-08-14 14:19:59 +02:00
# Find url to openslides
2012-05-31 00:27:47 +02:00
addr, port = detect_listen_opts(opts.address, opts.port)
if port == 80:
2012-08-14 14:19:59 +02:00
url = "http://%s" % addr
2012-05-31 00:27:47 +02:00
else:
url = "http://%s:%d" % (addr, port)
2012-08-14 14:19:59 +02:00
# Create Database if necessary
if not database_exists() or opts.syncdb:
run_syncdb()
set_system_url(url)
create_or_reset_admin_user()
# Reset Admin
elif opts.reset_admin:
create_or_reset_admin_user()
# Start OpenSlides
if opts.no_reload:
extra_args = ['--noreload']
else:
extra_args = []
start_openslides(addr, port, start_browser_url=url, extra_args=extra_args)
2012-11-01 10:56:38 +01:00
def create_settings(settings_path, database_path=None):
settings_module = os.path.dirname(settings_path)
if database_path is _portable_db_path:
database_path = get_portable_db_path()
dbpath_value = 'openslides.main.get_portable_db_path()'
else:
if database_path is None:
database_path = get_user_data_path('openslides', 'database.sqlite')
dbpath_value = repr(fs2unicode(database_path))
2012-11-01 10:56:38 +01:00
settings_content = CONFIG_TEMPLATE % dict(
2012-08-14 14:19:59 +02:00
default_key=base64.b64encode(os.urandom(KEY_LENGTH)),
dbpath=dbpath_value)
2012-11-01 10:56:38 +01:00
if not os.path.exists(settings_module):
os.makedirs(settings_module)
2012-08-14 14:19:59 +02:00
2012-11-01 10:56:38 +01:00
if not os.path.exists(os.path.dirname(database_path)):
os.makedirs(os.path.dirname(database_path))
2012-11-01 10:56:38 +01:00
with open(settings_path, 'w') as file:
file.write(settings_content)
2012-11-01 10:56:38 +01:00
def setup_django_environment(settings_path):
settings_file = os.path.basename(settings_path)
settings_module_name = "".join(settings_file.split('.')[:-1])
if '.' in settings_module_name:
print "'.' is not an allowed character in the settings-file"
sys.exit(1)
settings_module_dir = os.path.dirname(settings_path)
2012-11-01 10:56:38 +01:00
sys.path.append(settings_module_dir)
os.environ[django.conf.ENVIRONMENT_VARIABLE] = '%s' % settings_module_name
def detect_listen_opts(address, port):
if address is None:
try:
address = socket.gethostbyname(socket.gethostname())
except socket.error:
address = "127.0.0.1"
if port is None:
# test if we can use port 80
s = socket.socket()
port = 80
try:
s.bind((address, port))
s.listen(-1)
except socket.error:
port = 8000
finally:
s.close()
return address, port
2012-08-14 14:19:59 +02:00
def database_exists():
"""Detect if database exists"""
# can't be imported in global scope as they already require
# the settings module during import
from django.db import DatabaseError
2012-08-14 14:19:59 +02:00
from django.core.exceptions import ImproperlyConfigured
from openslides.participant.models import User
try:
2012-11-01 10:56:38 +01:00
# TODO: Use another model, the User could be deactivated
User.objects.count()
except DatabaseError:
2012-08-05 09:52:00 +02:00
return False
2012-08-14 14:19:59 +02:00
except ImproperlyConfigured:
print "Your settings file seems broken"
sys.exit(0)
2012-08-05 09:52:00 +02:00
else:
return True
2012-08-14 14:19:59 +02:00
def run_syncdb():
# now initialize the database
argv = ["", "syncdb", "--noinput"]
execute_from_command_line(argv)
execute_from_command_line(["", "loaddata", "groups_de"])
2012-05-31 00:27:47 +02:00
def set_system_url(url):
# can't be imported in global scope as it already requires
# the settings module during import
from openslides.config.models import config
key = "participant_pdf_system_url"
if key in config:
return
2012-05-31 00:27:47 +02:00
config[key] = url
2012-08-14 14:19:59 +02:00
def create_or_reset_admin_user():
# can't be imported in global scope as it already requires
# the settings module during import
from openslides.participant.models import User
try:
admin = User.objects.get(username="admin")
print("Password for user admin was reset to 'admin'")
except User.DoesNotExist:
admin = User()
admin.username = 'admin'
admin.last_name = 'Administrator'
2012-08-14 14:19:59 +02:00
print("Created default admin user")
admin.is_superuser = True
admin.default_password = 'admin'
admin.set_password(admin.default_password)
admin.save()
def start_openslides(addr, port, start_browser_url=None, extra_args=[]):
# Set the options
define('port', type=int, default=port)
define('address', default=addr)
2012-08-14 14:19:59 +02:00
# Open the browser
2012-08-14 14:19:59 +02:00
if start_browser_url:
start_browser(start_browser_url)
# Start the server
app = tornado.wsgi.WSGIContainer(django.core.handlers.wsgi.WSGIHandler())
tornado_app = tornado.web.Application(
[
('.*', tornado.web.FallbackHandler, dict(fallback=app)),
])
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(port=options.port,
address=options.address)
tornado.ioloop.IOLoop.instance().start()
2012-08-14 14:19:59 +02:00
def start_browser(url):
browser = webbrowser.get()
def f():
time.sleep(1)
browser.open(url)
t = threading.Thread(target=f)
t.start()
2012-11-24 20:35:06 +01:00
def fs2unicode(s):
if isinstance(s, unicode):
return s
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
return s.decode(fs_encoding)
def get_user_config_path(*args):
if sys.platform == "win32":
return win32_get_app_data_path(*args)
2012-11-24 20:35:06 +01:00
config_home = os.environ.get(
'XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
return os.path.join(fs2unicode(config_home), *args)
2012-11-24 20:35:06 +01:00
def get_user_data_path(*args):
if sys.platform == "win32":
return win32_get_app_data_path(*args)
2012-11-24 20:35:06 +01:00
data_home = os.environ.get(
'XDG_DATA_HOME', os.path.join(
os.path.expanduser('~'), '.local', 'share'))
return os.path.join(fs2unicode(data_home), *args)
2012-11-24 20:35:06 +01:00
def get_portable_path(*args):
# NOTE: sys.executable will be the path to openslides.exe
# since it is essentially a small wrapper that embeds the
# python interpreter
exename = os.path.basename(sys.executable).lower()
if exename != "openslides.exe":
2012-11-24 20:35:06 +01:00
raise Exception(
"Cannot determine portable path when "
"not running as portable")
portable_dir = fs2unicode(os.path.dirname(os.path.abspath(sys.executable)))
return os.path.join(portable_dir, *args)
2012-11-24 20:35:06 +01:00
def get_portable_db_path():
return get_portable_path('openslides', 'database.sqlite')
2012-11-24 20:35:06 +01:00
def win32_get_app_data_path(*args):
shell32 = ctypes.WinDLL("shell32.dll")
SHGetFolderPath = shell32.SHGetFolderPathW
2012-11-24 20:35:06 +01:00
SHGetFolderPath.argtypes = (
ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_uint32,
ctypes.c_wchar_p)
SHGetFolderPath.restype = ctypes.c_uint32
CSIDL_LOCAL_APPDATA = 0x001c
MAX_PATH = 260
buf = ctypes.create_unicode_buffer(MAX_PATH)
res = SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
if res != 0:
raise Exception("Could not deterime APPDATA path")
return os.path.join(buf.value, *args)
2012-08-14 14:19:59 +02:00
if __name__ == "__main__":
main()