summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-06-15 01:09:39 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-06-15 01:09:39 -0400
commit9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b (patch)
tree53a1f25228d86a03825f50158d50ccc0f651df40
parent85a3c85a887e4caf21a8d79b71fe41cc94aacc64 (diff)
downloadaskbot-9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b.tar.gz
askbot-9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b.tar.bz2
askbot-9d2c4337d8ae0ec8c9f1903dcc3b613c80449b5b.zip
basic deployment script works
-rw-r--r--askbot/deployment/__init__.py101
-rw-r--r--askbot/deployment/dialogs.py20
-rw-r--r--askbot/deployment/messages.py91
-rw-r--r--askbot/deployment/path_utils.py146
-rw-r--r--askbot/doc/INSTALL98
-rw-r--r--askbot/setup_templates/settings.py12
-rw-r--r--askbot/upfiles/README3
-rw-r--r--askbot/urls.py3
-rw-r--r--setup.py15
9 files changed, 435 insertions, 54 deletions
diff --git a/askbot/deployment/__init__.py b/askbot/deployment/__init__.py
new file mode 100644
index 00000000..9f4ebaf9
--- /dev/null
+++ b/askbot/deployment/__init__.py
@@ -0,0 +1,101 @@
+"""
+module for deploying askbot
+"""
+import os.path
+from askbot.deployment import messages
+from askbot.deployment import dialogs
+from askbot.deployment import path_utils
+
+def startforum():
+ """basic deployment procedure
+ asks user several questions, then either creates
+ new deployment (in the case of new installation)
+ or gives hints on how to add askbot to an existing
+ Django project
+ """
+ #ask
+ print messages.DEPLOY_PREAMBLE
+
+ directory = None #directory where to put stuff
+ create_new = False #create new django project or not
+ where_to_deploy_msg = messages.WHERE_TO_DEPLOY
+ while directory is None:
+
+ directory = raw_input(where_to_deploy_msg + ' ')
+
+ where_to_deploy_msg = messages.WHERE_TO_DEPLOY_QUIT
+
+ directory = os.path.normpath(directory)
+ directory = os.path.abspath(directory)
+
+ if os.path.isfile(directory):
+ print messages.CANT_INSTALL_INTO_FILE % {'path':directory}
+ directory = None
+ continue
+
+ if path_utils.can_create_path(directory):
+ if os.path.exists(directory):
+ if path_utils.path_is_clean_for_django(directory):
+ if path_utils.has_existing_django_project(directory):
+ message = messages.SHOULD_ADD_APP_HERE % \
+ {
+ 'path': directory
+ }
+ should_add_app = dialogs.multiple_choice_input(
+ message,
+ options = ['yes','no']
+ )
+ if should_add_app == 'yes':
+ assert(create_new == False)
+ if path_utils.dir_name_acceptable(directory):
+ break
+ else:
+ print messages.format_msg_bad_dir_name(directory)
+ directory = None
+ continue
+ else:
+ directory = None
+ continue
+ else:
+ assert(directory != None)
+ if path_utils.dir_name_acceptable(directory):
+ create_new = True
+ break
+ else:
+ print messages.format_msg_bad_dir_name(directory)
+ directory = None
+ continue
+ else:
+ print messages.format_msg_dir_unclean_django(directory)
+ directory = None
+ continue
+ else:
+ message = messages.format_msg_create(directory)
+ should_create_new = dialogs.multiple_choice_input(
+ message,
+ options = ['yes','no']
+ )
+ if should_create_new == 'yes':
+ if path_utils.dir_name_acceptable(directory):
+ create_new = True
+ break
+ else:
+ print messages.format_msg_bad_dir_name(directory)
+ directory = None
+ continue
+ else:
+ directory = None
+ continue
+ else:
+ print messages.format_msg_dir_not_writable(directory)
+ directory = None
+ continue
+
+ help_file = os.path.join(directory, 'askbot', 'doc', 'INSTALL')
+ if create_new:
+ path_utils.create_path(directory)
+ path_utils.deploy_into(directory, new_project = True)
+ print messages.HOW_TO_DEPLOY_NEW % {'help_file': help_file}
+ else:
+ path_utils.deploy_into(directory, new_project = False)
+ print messages.HOW_TO_ADD_ASKBOT_TO_DJANGO % {'help_file': help_file}
diff --git a/askbot/deployment/dialogs.py b/askbot/deployment/dialogs.py
new file mode 100644
index 00000000..40f0d2ee
--- /dev/null
+++ b/askbot/deployment/dialogs.py
@@ -0,0 +1,20 @@
+"""functions that directly handle user input
+"""
+from askbot.deployment import messages
+import time
+
+def multiple_choice_input(prompt_phrase, options = None):
+ """prints a prompt, accepts keyboard input
+ and makes sure that user response is one of given
+ in the options argument, which is required
+ and must be a list
+ """
+ assert(isinstance(options, list))
+ while 1:
+ response = raw_input('\n%s (type %s): ' % (prompt_phrase, '/'.join(options)))
+ if response in options:
+ return response
+ else:
+ opt_string = ','.join(options)
+ print messages.INVALID_INPUT % {'opt_string': opt_string}
+ time.sleep(1)
diff --git a/askbot/deployment/messages.py b/askbot/deployment/messages.py
new file mode 100644
index 00000000..1569de6c
--- /dev/null
+++ b/askbot/deployment/messages.py
@@ -0,0 +1,91 @@
+"""Messages used in the procedure of deploying Askbot
+"""
+import os.path
+from askbot.deployment import path_utils
+
+DEPLOY_PREAMBLE = """
+Deploying Askbot - Django Q&A forum application
+Problems installing? -> please email admin@askbot.org
+
+To CANCEL - hit Ctr-C at any time"""
+
+WHERE_TO_DEPLOY = 'Where to deploy (in which directory)?'
+
+WHERE_TO_DEPLOY_QUIT = 'Where deploy forum (directory)? (Ctrl-C to quit)'
+
+CANT_INSTALL_INTO_FILE = '%(path)s is a file\ncannot install there'
+
+SHOULD_ADD_APP_HERE = 'Directory %(path)s?\nalready has a Django ' \
+ + 'project - do you want to add askbot app to that project?'
+
+HOW_TO_DEPLOY_NEW = 'Done. Please find further instructions in the file below:'\
+ + '\n%(help_file)s'
+
+HOW_TO_ADD_ASKBOT_TO_DJANGO = HOW_TO_DEPLOY_NEW
+
+DIR_IS_NOT_WRITABLE = 'Directory %(dir)s is not writable'
+
+PARENT_DIR_IS_NOT_WRITABLE = """To create directory %(target_dir)s
+we need to add %(non_existing_tail)s to %(existing_prefix)s
+but %(existing_prefix)s is not writable"""
+
+CONFIRM_DIR_CREATION = """Adding new directories:\n%(existing_prefix)s <-/%(non_existing_tail)s
+Accept?"""
+
+INVALID_INPUT = 'Please type one of: %(opt_string)s ' \
+ + '(or hit Ctrl-C to quit)'
+
+DIR_NAME_TAKEN_BY_PYTHON = """Directory '%(dir)s' is aready used by other Python module.
+Please choose some other name for your django project"""
+
+DIR_NAME_TAKEN_BY_ASKBOT = """Please do not name your entire Django project 'askbot',
+because this name is already used by the askbot app itself"""
+
+def format_msg_dir_not_writable(directory):
+ """returns a meaningful message explaining why directory
+ is not writable by the user
+ """
+ if os.path.exists(directory):
+ if path_utils.directory_is_writable(directory):
+ return ''
+ else:
+ return DIR_IS_NOT_WRITABLE % {'dir': directory}
+ else:
+ prefix, tail = path_utils.split_at_break_point(directory)
+ data = {
+ 'existing_prefix': prefix,
+ 'non_existing_tail': tail,
+ 'target_dir': directory
+ }
+ return PARENT_DIR_IS_NOT_WRITABLE % data
+
+def format_msg_create(directory):
+ """returns a message explaining wha directories
+ are about to be created and asks user if they want to proceed
+ """
+ if os.path.exists(directory):
+ raise Exception('directory %s aready exists' % directory)
+ else:
+ prefix, tail = path_utils.split_at_break_point(directory)
+ data = {
+ 'existing_prefix': prefix,
+ 'non_existing_tail': tail,
+ }
+ return CONFIRM_DIR_CREATION % data
+
+def format_msg_dir_unclean_django(directory):
+ """retuns a message telling which of the parent
+ directories contains a django project
+ so that users don't create nested projects
+ """
+ parent_django_dir = path_utils.find_parent_dir_with_django(directory)
+
+def format_msg_bad_dir_name(directory):
+ """directory name must be bad - i.e. taken by other python module
+ on PYTHONPATH
+ """
+ dir_name = os.path.basename(directory)
+ if dir_name == 'askbot':
+ return DIR_NAME_TAKEN_BY_ASKBOT
+ else:
+ return DIR_NAME_TAKEN_BY_PYTHON % {'dir': dir_name}
diff --git a/askbot/deployment/path_utils.py b/askbot/deployment/path_utils.py
new file mode 100644
index 00000000..71e66182
--- /dev/null
+++ b/askbot/deployment/path_utils.py
@@ -0,0 +1,146 @@
+"""utilities in addition to os.path
+that
+* help to test existing paths on usability for the installation
+* create necessary directories
+* install deployment files
+"""
+import os
+import os.path
+import tempfile
+import re
+import glob
+import shutil
+import imp
+
+def split_at_break_point(directory):
+ """splits directory path into two pieces
+ first that exists and secon - that does not
+ by determining a point at which path breaks
+
+ exception will be raised if directory in fact exists
+ """
+ assert(os.path.exists(directory) == False)
+
+ head = directory
+ tail_bits = list()
+ while os.path.exists(head) == False:
+ head, tail = os.path.split(head)
+ tail_bits.insert(0, tail)
+ return head, os.path.join(*tail_bits)
+
+
+def directory_is_writable(directory):
+ """returns True if directory exists
+ and is writable, False otherwise
+ """
+ tempfile.tempdir = directory
+ try:
+ #run writability test
+ temp_path = tempfile.mktemp()
+ assert(os.path.dirname(temp_path) == directory)
+ temp_file = open(temp_path, 'w')
+ temp_file.close()
+ os.unlink(temp_path)
+ return True
+ except IOError:
+ return False
+
+
+def can_create_path(directory):
+ """returns True if user can write file into
+ directory even if it does not exist yet
+ and False otherwise
+ """
+ if os.path.exists(directory):
+ if not os.path.isdir(directory):
+ return False
+ else:
+ directory, junk = split_at_break_point(directory)
+ return directory_is_writable(directory)
+
+
+IMPORT_RE1 = re.compile(r'from django.*import')
+IMPORT_RE2 = re.compile(r'import django')
+def has_existing_django_project(directory):
+ """returns True is any of the .py files
+ in a given directory imports anything from django
+ """
+ file_list = glob.glob(directory + '*.py')
+ for file_name in file_list:
+ py_file = open(file_name)
+ for line in py_file:
+ if IMPORT_RE1.match(line) or IMPORT_RE2.match(line):
+ py_file.close()
+ return True
+ py_file.close()
+ return False
+
+
+def find_parent_dir_with_django(directory):
+ """returns path to Django project anywhere
+ above the directory
+ if nothing is found returns None
+ """
+ parent_dir = os.path.dirname(directory)
+ while parent_dir != directory:
+ if has_existing_django_project(parent_dir):
+ return parent_dir
+ else:
+ directory = parent_dir
+ parent_dir = os.path.dirname(directory)
+ return None
+
+
+def path_is_clean_for_django(directory):
+ """returns False if any of the parent directories
+ contains a Django project, otherwise True
+ does not check the current directory
+ """
+ django_dir = find_parent_dir_with_django(directory)
+ return (django_dir is None)
+
+
+def create_path(directory):
+ if os.path.isdir(directory):
+ return
+ elif os.path.exists(directory):
+ raise ValueError('expect directory or a non-existing path')
+ else:
+ os.makedirs(directory)
+
+SOURCE_DIR = os.path.dirname(os.path.dirname(__file__))
+def deploy_into(directory, new_project = None):
+ """will copy necessary files into the directory
+ """
+ assert(new_project is not None)
+ if new_project:
+ copy_files = ('__init__.py', 'settings.py', 'manage.py', 'urls.py')
+ print 'copying files: ',
+ for file_name in copy_files:
+ src = os.path.join(SOURCE_DIR, 'setup_templates', file_name)
+ print '%s ' % file_name,
+ shutil.copy(src, directory)
+ #copy log directory
+ src = os.path.join(SOURCE_DIR, 'setup_templates', 'log')
+ dst = os.path.join(directory, 'log')
+ shutil.copytree(src, dst)
+
+ print ''
+ app_dir = os.path.join(directory, 'askbot')
+
+ print 'copying directories: ',
+ copy_dirs = ('doc','cron','upfiles')
+ for dir_name in copy_dirs:
+ src = os.path.join(SOURCE_DIR, dir_name)
+ dst = os.path.join(app_dir, dir_name)
+ print dir_name + ' ',
+ shutil.copytree(src, dst)
+ print ''
+
+def dir_name_acceptable(directory):
+ dir_name = os.path.basename(directory)
+ try:
+ imp.find_module(dir_name)
+ return False
+ except ImportError:
+ return True
diff --git a/askbot/doc/INSTALL b/askbot/doc/INSTALL
index 55b8614d..4589e1ba 100644
--- a/askbot/doc/INSTALL
+++ b/askbot/doc/INSTALL
@@ -1,7 +1,7 @@
CONTENTS
------------------
-A. PREREQUISITES
-B. INSTALLATION
+A. INSTALLATION
+B. DEPLOYMENT
1. Settings file
2. Database
3. Running Askbot in the development server
@@ -14,67 +14,69 @@ C. CONFIGURATION PARAMETERS (settings_local.py)
D. CUSTOMIZATION
-A. PREREQUISITES
------------------------------------------------
-Note: by default all installation activity is made in the superuser 'root' account.
-This can be achieved either by logging in as root (su root),
-or - if you have program sudo installed - prefix all commands with sudo.
-So sodo will be listed below as optional.
+A. INSTALLATION
+
+NOTE: if you need to install in non-standard locations
+please take a look here: http://askbot.org/wiki/index.php/Installing_software_components_at_non-standard_locations
+
+type
+
+> python setup.py install
+
+or
+
+> easy_install askbot
+
+then, if you need to start a new deployment, type
+
+> startforum
+
+Please read a few paragraphs of the following section on
+how to initialize the application
+
+ATTENTION WINDOWS USERS: you will need to manually install
+binary for mysql-python from here
+http://www.codegood.com/archives/4
+
+B. DEPLOYMENT
+
+if you are adding askbot to your existing project that already
+has other apps, you'll need to "merge" some of the files in
+directory askbot/install_templates into your corresponding project files
+more instuctions about this are to follow soon - if you have questions
+ask on the forum
+
+for new deployment
+> startforum
+
+and answer questions in the end you should be able to get almost
+working installation, you'll need to edit file settings.py
+and at least enter database name, user name and password
+
+then type
+
+> python manage.py syncdb
+> python manage.py migrate forum
-0. We recommend you to use python-setuptools to install pre-requirement libraries.
-If you haven't installed it, please try to install it first.
-e.g, [sudo] apt-get install python-setuptools
+now run:
-1. Python2.5/2.6, Django v1.1.1
+> python manage.py runserver `hostname -i`:8000 #or use some other port
-1A If you are using MySQL, mysql client for python must be installed
-[sudo] easy_install mysql-python
+command `hostname -i` returns IP address on Unix, you can type the IP by hand too
-2. Python-openid v2.2
-http://openidenabled.com/python-openid/
-[sudo] easy_install python-openid
-4. html5lib
-http://code.google.com/p/html5lib/
-Used for HTML sanitizer
-[sudo] easy_install html5lib
-5. Markdown2
-http://code.google.com/p/python-markdown2/
-[sudo] easy_install markdown2
-6. Django Debug Toolbar
-http://github.com/robhudson/django-debug-toolbar/tree/master
-7. djangosphinx (optional - for full text questions+answer+tag)
-http://github.com/dcramer/django-sphinx/tree/master/djangosphinx
-8. sphinx search engine (optional, works together with djangosphinx)
-http://sphinxsearch.com/downloads.html
+NOTES BELOW ARE OUTDATED, but may be useful
+if you have questions - please ask at http://askbot.org
-9. recaptcha_django (installed through svn)
-http://code.google.com/p/recaptcha-django/
-10. python recaptcha module
-http://code.google.com/p/recaptcha/
-easy_install recaptcha-client
-Notice that you will need to register with recaptcha.net and receive
-recaptcha public and private keys that need to be saved in your
-settings_local.py file
-11. South
-http://south.aeracode.org/docs/installation.html
-Used for database schema and data migrations
-[sudo] easy_install South
-EXTRA DEPENDENCIES FOR PYTHON 2.4
-* hashlib (made standard in python 2.5)
-NOTES: django_authopenid is included into Askbot code
-and is significantly modified. http://code.google.com/p/django-authopenid/
-no need to install this library
-B. INSTALLATION
NOTE: If you want to upgrade software, not install from scratch -
take a look into forum/documentation/UPGRADE
diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py
index ec3a511f..aa6194aa 100644
--- a/askbot/setup_templates/settings.py
+++ b/askbot/setup_templates/settings.py
@@ -13,9 +13,9 @@ ADMINS = (
MANAGERS = ADMINS
DATABASE_ENGINE = 'mysql' # only mysql is supported, more will be in the near future
-DATABASE_NAME = 'junk' # Or path to database file if using sqlite3.
-DATABASE_USER = 'junk' # Not used with sqlite3.
-DATABASE_PASSWORD = 'secret' # Not used with sqlite3.
+DATABASE_NAME = '' # Or path to database file if using sqlite3.
+DATABASE_USER = '' # Not used with sqlite3.
+DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
@@ -51,12 +51,14 @@ LANGUAGE_CODE = 'en'
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = ''
+MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'askbot', 'upfiles')
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
-MEDIA_URL = ''
+MEDIA_URL = ''#set this to serve uploaded files correctly
+
+PROJECT_ROOT = os.path.dirname(__file__)
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
diff --git a/askbot/upfiles/README b/askbot/upfiles/README
new file mode 100644
index 00000000..097ee8a5
--- /dev/null
+++ b/askbot/upfiles/README
@@ -0,0 +1,3 @@
+this directory is empty
+it is copied at deployment time to some other
+location where it will hold user uploaded files
diff --git a/askbot/urls.py b/askbot/urls.py
index 8a4f9b29..c538df3b 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -9,6 +9,7 @@ from askbot import views as app
from askbot.feed import RssLastestQuestionsFeed
from askbot.sitemap import QuestionsSitemap
from django.utils.translation import ugettext as _
+from django.conf import settings
admin.autodiscover()
feeds = {
@@ -36,7 +37,7 @@ urlpatterns = patterns('',
url(
r'^%s(?P<path>.*)$' % _('upfiles/'),
'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH,'upfiles').replace('\\','/')},
+ {'document_root': os.path.join(settings.PROJECT_ROOT, 'askbot', 'upfiles').replace('\\','/')},
name='uploaded_file',
),
url(r'^%s$' % _('about/'), app.meta.about, name='about'),
diff --git a/setup.py b/setup.py
index 0afe72b2..2074d27b 100644
--- a/setup.py
+++ b/setup.py
@@ -25,6 +25,11 @@ setup(
author_email = 'evgeny.fadeev@gmail.com',
license = 'GPLv3',
keywords = 'forum, community, wiki, Q&A',
+ entry_points = {
+ 'console_scripts' : [
+ 'startforum = askbot.deployment:startforum',
+ ]
+ },
url = 'http://askbot.org',
include_package_data = True,
install_requires = install_requires,
@@ -37,3 +42,13 @@ setup(
if sys.platform in WIN_PLATFORMS:
print 'ATTENTION!! please install windows binary mysql-python package'
print 'at http://www.codegood.com/archives/4'
+
+print '**************************************************************'
+print '* *'
+print '* Thanks for installing Askbot. *'
+print '* To start deploying type: >startforum *'
+print '* Please take a look at the manual askbot/doc/INSTALL *'
+print '* And please do not hesitate to ask your questions at *'
+print '* at http://askbot.org *'
+print '* *'
+print '**************************************************************'