manage.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #!/usr/bin/env python
  2. """
  3. flaskbb.manage
  4. ~~~~~~~~~~~~~~~~~~~~
  5. This script provides some easy to use commands for
  6. creating the database with or without some sample content.
  7. You can also run the development server with it.
  8. Just type `python manage.py` to see the full list of commands.
  9. TODO: When Flask 1.0 is released, get rid of Flask-Script and use click.
  10. Then it's also possible to split the commands in "command groups"
  11. which would make the commands better seperated from each other
  12. and less confusing.
  13. :copyright: (c) 2014 by the FlaskBB Team.
  14. :license: BSD, see LICENSE for more details.
  15. """
  16. from __future__ import print_function
  17. import sys
  18. import os
  19. import subprocess
  20. import requests
  21. import time
  22. from flask import current_app
  23. from werkzeug.utils import import_string
  24. from sqlalchemy.exc import IntegrityError, OperationalError
  25. from flask_script import (Manager, Shell, Server, prompt, prompt_pass,
  26. prompt_bool)
  27. from flask_migrate import MigrateCommand, upgrade
  28. from flaskbb import create_app
  29. from flaskbb.extensions import db, plugin_manager, whooshee
  30. from flaskbb.utils.populate import (create_test_data, create_welcome_forum,
  31. create_admin_user, create_default_groups,
  32. create_default_settings, insert_bulk_data,
  33. update_settings_from_fixture)
  34. # Use the development configuration if available
  35. try:
  36. from flaskbb.configs.development import DevelopmentConfig as Config
  37. except ImportError:
  38. try:
  39. from flaskbb.configs.production import ProductionConfig as Config
  40. except ImportError:
  41. from flaskbb.configs.default import DefaultConfig as Config
  42. app = create_app(Config)
  43. manager = Manager(app)
  44. # Used to get the plugin translations
  45. PLUGINS_FOLDER = os.path.join(app.root_path, "plugins")
  46. # Run local server
  47. manager.add_command("runserver", Server("localhost", port=8080))
  48. # Migration commands
  49. manager.add_command('db', MigrateCommand)
  50. # Add interactive project shell
  51. def make_shell_context():
  52. return dict(app=current_app, db=db)
  53. manager.add_command("shell", Shell(make_context=make_shell_context))
  54. @manager.command
  55. def initdb(default_settings=True):
  56. """Creates the database."""
  57. upgrade()
  58. if default_settings:
  59. print("Creating default data...")
  60. create_default_groups()
  61. create_default_settings()
  62. @manager.command
  63. def dropdb():
  64. """Deletes the database."""
  65. db.drop_all()
  66. @manager.command
  67. def populate(dropdb=False, createdb=False):
  68. """Creates the database with some default data.
  69. To drop or create the databse use the '-d' or '-c' options.
  70. """
  71. if dropdb:
  72. print("Dropping database...")
  73. db.drop_all()
  74. if createdb:
  75. print("Creating database...")
  76. upgrade()
  77. print("Creating test data...")
  78. create_test_data()
  79. @manager.command
  80. def reindex():
  81. """Reindexes the search index."""
  82. print("Reindexing search index...")
  83. whooshee.reindex()
  84. @manager.option('-u', '--username', dest='username')
  85. @manager.option('-p', '--password', dest='password')
  86. @manager.option('-e', '--email', dest='email')
  87. def create_admin(username=None, password=None, email=None):
  88. """Creates the admin user."""
  89. if not (username and password and email):
  90. username = prompt("Username")
  91. email = prompt("A valid email address")
  92. password = prompt_pass("Password")
  93. create_admin_user(username=username, password=password, email=email)
  94. @manager.option('-u', '--username', dest='username')
  95. @manager.option('-p', '--password', dest='password')
  96. @manager.option('-e', '--email', dest='email')
  97. def install(username=None, password=None, email=None):
  98. """Installs FlaskBB with all necessary data."""
  99. print("Creating default data...")
  100. try:
  101. create_default_groups()
  102. create_default_settings()
  103. except IntegrityError:
  104. print("Couldn't create the default data because it already exist!")
  105. if prompt_bool("Found an existing database."
  106. "Do you want to recreate the database? (y/n)"):
  107. db.session.rollback()
  108. db.drop_all()
  109. upgrade()
  110. create_default_groups()
  111. create_default_settings()
  112. else:
  113. sys.exit(0)
  114. except OperationalError:
  115. print("No database found.")
  116. if prompt_bool("Do you want to create the database now? (y/n)"):
  117. db.session.rollback()
  118. upgrade()
  119. create_default_groups()
  120. create_default_settings()
  121. else:
  122. sys.exit(0)
  123. print("Creating admin user...")
  124. if username and password and email:
  125. create_admin_user(username=username, password=password, email=email)
  126. else:
  127. create_admin()
  128. print("Creating welcome forum...")
  129. create_welcome_forum()
  130. print("Compiling translations...")
  131. compile_translations()
  132. if prompt_bool("Do you want to use Emojis? (y/n)"):
  133. print("Downloading emojis. This can take a few minutes.")
  134. download_emoji()
  135. print("Congratulations! FlaskBB has been successfully installed")
  136. @manager.option('-t', '--topics', dest="topics", default=100)
  137. @manager.option('-p', '--posts', dest="posts", default=100)
  138. def insertbulkdata(topics, posts):
  139. """Warning: This can take a long time!.
  140. Creates 100 topics and each topic contains 100 posts.
  141. """
  142. timer = time.time()
  143. created_topics, created_posts = insert_bulk_data(int(topics), int(posts))
  144. elapsed = time.time() - timer
  145. print("It took {time} seconds to create {topics} topics and "
  146. "{posts} posts"
  147. .format(time=elapsed, topics=created_topics, posts=created_posts))
  148. @manager.option('-f', '--force', dest="force", default=False)
  149. @manager.option('-s', '--settings', dest="settings")
  150. def update(settings=None, force=False):
  151. """Updates the settings via a fixture. All fixtures have to be placed
  152. in the `fixture`.
  153. Usage: python manage.py update -s your_fixture
  154. """
  155. if settings is None:
  156. settings = "settings"
  157. try:
  158. fixture = import_string(
  159. "flaskbb.fixtures.{}".format(settings)
  160. )
  161. fixture = fixture.fixture
  162. except ImportError:
  163. raise "{} fixture is not available".format(settings)
  164. overwrite_group = overwrite_setting = False
  165. if force:
  166. overwrite_group = overwrite_setting = True
  167. count = update_settings_from_fixture(
  168. fixture=fixture,
  169. overwrite_group=overwrite_group,
  170. overwrite_setting=overwrite_setting
  171. )
  172. print("{} groups and {} settings updated.".format(
  173. len(count.keys()), len(count.values()))
  174. )
  175. @manager.command
  176. def update_translations():
  177. """Updates all translations."""
  178. # update flaskbb translations
  179. translations_folder = os.path.join(app.root_path, "translations")
  180. source_file = os.path.join(translations_folder, "messages.pot")
  181. subprocess.call(["pybabel", "extract", "-F", "babel.cfg",
  182. "-k", "lazy_gettext", "-o", source_file, "."])
  183. subprocess.call(["pybabel", "update", "-i", source_file,
  184. "-d", translations_folder])
  185. # updates all plugin translations too
  186. for plugin in plugin_manager.all_plugins:
  187. update_plugin_translations(plugin)
  188. @manager.command
  189. def add_translations(translation):
  190. """Adds a new language to the translations."""
  191. translations_folder = os.path.join(app.root_path, "translations")
  192. source_file = os.path.join(translations_folder, "messages.pot")
  193. subprocess.call(["pybabel", "extract", "-F", "babel.cfg",
  194. "-k", "lazy_gettext", "-o", source_file, "."])
  195. subprocess.call(["pybabel", "init", "-i", source_file,
  196. "-d", translations_folder, "-l", translation])
  197. @manager.command
  198. def compile_translations():
  199. """Compiles all translations."""
  200. # compile flaskbb translations
  201. translations_folder = os.path.join(app.root_path, "translations")
  202. subprocess.call(["pybabel", "compile", "-d", translations_folder])
  203. # compile all plugin translations
  204. for plugin in plugin_manager.all_plugins:
  205. compile_plugin_translations(plugin)
  206. # Plugin translation commands
  207. @manager.command
  208. def add_plugin_translations(plugin, translation):
  209. """Adds a new language to the plugin translations. Expects the name
  210. of the plugin and the translations name like "en".
  211. """
  212. plugin_folder = os.path.join(PLUGINS_FOLDER, plugin)
  213. translations_folder = os.path.join(plugin_folder, "translations")
  214. source_file = os.path.join(translations_folder, "messages.pot")
  215. subprocess.call(["pybabel", "extract", "-F", "babel.cfg",
  216. "-k", "lazy_gettext", "-o", source_file,
  217. plugin_folder])
  218. subprocess.call(["pybabel", "init", "-i", source_file,
  219. "-d", translations_folder, "-l", translation])
  220. @manager.command
  221. def update_plugin_translations(plugin):
  222. """Updates the plugin translations. Expects the name of the plugin."""
  223. plugin_folder = os.path.join(PLUGINS_FOLDER, plugin)
  224. translations_folder = os.path.join(plugin_folder, "translations")
  225. source_file = os.path.join(translations_folder, "messages.pot")
  226. subprocess.call(["pybabel", "extract", "-F", "babel.cfg",
  227. "-k", "lazy_gettext", "-o", source_file,
  228. plugin_folder])
  229. subprocess.call(["pybabel", "update", "-i", source_file,
  230. "-d", translations_folder])
  231. @manager.command
  232. def compile_plugin_translations(plugin):
  233. """Compile the plugin translations. Expects the name of the plugin."""
  234. plugin_folder = os.path.join(PLUGINS_FOLDER, plugin)
  235. translations_folder = os.path.join(plugin_folder, "translations")
  236. subprocess.call(["pybabel", "compile", "-d", translations_folder])
  237. @manager.command
  238. def download_emoji():
  239. """Downloads emojis from emoji-cheat-sheet.com."""
  240. HOSTNAME = "https://api.github.com"
  241. REPO = "/repos/arvida/emoji-cheat-sheet.com/contents/public/graphics/emojis"
  242. FULL_URL = "{}{}".format(HOSTNAME, REPO)
  243. DOWNLOAD_PATH = os.path.join(app.static_folder, "emoji")
  244. response = requests.get(FULL_URL)
  245. cached_count = 0
  246. count = 0
  247. for image in response.json():
  248. if not os.path.exists(os.path.abspath(DOWNLOAD_PATH)):
  249. print("{} does not exist.".format(os.path.abspath(DOWNLOAD_PATH)))
  250. sys.exit(1)
  251. full_path = os.path.join(DOWNLOAD_PATH, image["name"])
  252. if not os.path.exists(full_path):
  253. count += 1
  254. f = open(full_path, 'wb')
  255. f.write(requests.get(image["download_url"]).content)
  256. f.close()
  257. if count == cached_count + 50:
  258. cached_count = count
  259. print("{} out of {} Emojis downloaded...".format(
  260. cached_count, len(response.json())))
  261. print("Finished downloading {} Emojis.".format(count))
  262. if __name__ == "__main__":
  263. manager.run()