123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- """
- Console utility to help manage F.A.B's apps
- use:
- $ fabmanager --help
- """
- from io import BytesIO
- import os
- import shutil
- import sys
- from zipfile import ZipFile
- import click
- from . import const as c
- try:
- # For Python 3.0 and later
- from urllib.request import urlopen
- except ImportError:
- # Fall back to Python 2's urllib2
- from urllib2 import urlopen
- click.echo(
- click.style(
- "fabmanager is going to be deprecated in 2.2.X, you can use "
- "the same commands on the improved 'flask fab <command>'",
- fg="red",
- )
- )
- SQLA_REPO_URL = (
- "https://github.com/dpgaspar/Flask-AppBuilder-Skeleton/archive/master.zip"
- )
- MONGOENGIE_REPO_URL = (
- "https://github.com/dpgaspar/Flask-AppBuilder-Skeleton-me/archive/master.zip"
- )
- ADDON_REPO_URL = (
- "https://github.com/dpgaspar/Flask-AppBuilder-Skeleton-AddOn/archive/master.zip"
- )
- def import_application(app_package, appbuilder):
- sys.path.append(os.getcwd())
- try:
- _app = __import__(app_package)
- except Exception as e:
- click.echo(
- click.style(
- "Was unable to import {0} Error: {1}".format(app_package, e), fg="red"
- )
- )
- exit(3)
- if hasattr(_app, appbuilder):
- return getattr(_app, appbuilder)
- else:
- click.echo(
- click.style(
- "There is no appbuilder var on your package, "
- "you can use appbuilder parameter to config",
- fg="red",
- )
- )
- exit(3)
- def echo_header(title):
- click.echo(click.style(title, fg="green"))
- click.echo(click.style("-" * len(title), fg="green"))
- @click.group()
- def cli_app():
- """
- This is a set of commands to ease the creation and maintenance
- of your flask-appbuilder applications.
- All commands that import your app will assume by default that
- you're running on your projects directory just before the app directory.
- They will also assume that __init__.py initializes AppBuilder
- like this (using a var named appbuilder) just like the skeleton app::
- appbuilder = AppBuilder(......)
- If you're using different namings use app and appbuilder parameters.
- """
- pass
- @cli_app.command("reset-password")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- @click.option(
- "--username",
- default="admin",
- prompt="The username",
- help="Resets the password for a particular user.",
- )
- @click.password_option()
- def reset_password(app, appbuilder, username, password):
- """
- Resets a user's password
- """
- _appbuilder = import_application(app, appbuilder)
- user = _appbuilder.sm.find_user(username=username)
- if not user:
- click.echo("User {0} not found.".format(username))
- else:
- _appbuilder.sm.reset_password(user.id, password)
- click.echo(click.style("User {0} reseted.".format(username), fg="green"))
- @cli_app.command("create-admin")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- @click.option("--username", default="admin", prompt="Username")
- @click.option("--firstname", default="admin", prompt="User first name")
- @click.option("--lastname", default="user", prompt="User last name")
- @click.option("--email", default="admin@fab.org", prompt="Email")
- @click.password_option()
- def create_admin(app, appbuilder, username, firstname, lastname, email, password):
- """
- Creates an admin user
- """
- auth_type = {
- c.AUTH_DB: "Database Authentications",
- c.AUTH_OID: "OpenID Authentication",
- c.AUTH_LDAP: "LDAP Authentication",
- c.AUTH_REMOTE_USER: "WebServer REMOTE_USER Authentication",
- c.AUTH_OAUTH: "OAuth Authentication",
- }
- _appbuilder = import_application(app, appbuilder)
- click.echo(
- click.style(
- "Recognized {0}.".format(
- auth_type.get(_appbuilder.sm.auth_type, "No Auth method")
- ),
- fg="green",
- )
- )
- role_admin = _appbuilder.sm.find_role(_appbuilder.sm.auth_role_admin)
- user = _appbuilder.sm.add_user(
- username, firstname, lastname, email, role_admin, password
- )
- if user:
- click.echo(click.style("Admin User {0} created.".format(username), fg="green"))
- else:
- click.echo(click.style("No user created an error occured", fg="red"))
- @cli_app.command("create-user")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- @click.option("--role", default="Public", prompt="Role")
- @click.option("--username", prompt="Username")
- @click.option("--firstname", prompt="User first name")
- @click.option("--lastname", prompt="User last name")
- @click.option("--email", prompt="Email")
- @click.password_option()
- def create_user(app, appbuilder, role, username, firstname, lastname, email, password):
- """
- Create a user
- """
- _appbuilder = import_application(app, appbuilder)
- role_object = _appbuilder.sm.find_role(role)
- user = _appbuilder.sm.add_user(
- username, firstname, lastname, email, role_object, password
- )
- if user:
- click.echo(click.style("User {0} created.".format(username), fg="green"))
- else:
- click.echo(click.style("Error! No user created", fg="red"))
- @cli_app.command("run")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- @click.option("--host", default="0.0.0.0")
- @click.option("--port", default=8080)
- @click.option("--debug", default=True)
- def run(app, appbuilder, host, port, debug):
- """
- Runs Flask dev web server.
- """
- _appbuilder = import_application(app, appbuilder)
- _appbuilder.get_app.run(host=host, port=port, debug=debug)
- @cli_app.command("create-db")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- def create_db(app, appbuilder):
- """
- Create all your database objects (SQLAlchemy specific).
- """
- from flask_appbuilder.models.sqla import Base
- _appbuilder = import_application(app, appbuilder)
- engine = _appbuilder.get_session.get_bind(mapper=None, clause=None)
- Base.metadata.create_all(engine)
- click.echo(click.style("DB objects created", fg="green"))
- @cli_app.command("version")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- def version(app, appbuilder):
- """
- Flask-AppBuilder package version
- """
- _appbuilder = import_application(app, appbuilder)
- click.echo(
- click.style(
- "F.A.B Version: {0}.".format(_appbuilder.version), bg="blue", fg="white"
- )
- )
- @cli_app.command("security-cleanup")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- def security_cleanup(app, appbuilder):
- """
- Cleanup unused permissions from views and roles.
- """
- _appbuilder = import_application(app, appbuilder)
- _appbuilder.security_cleanup()
- click.echo(click.style("Finished security cleanup", fg="green"))
- @cli_app.command("list-views")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- def list_views(app, appbuilder):
- """
- List all registered views
- """
- _appbuilder = import_application(app, appbuilder)
- echo_header("List of registered views")
- for view in _appbuilder.baseviews:
- click.echo(
- "View:{0} | Route:{1} | Perms:{2}".format(
- view.__class__.__name__, view.route_base, view.base_permissions
- )
- )
- @cli_app.command("list-users")
- @click.option("--app", default="app", help="Your application init directory (package)")
- @click.option("--appbuilder", default="appbuilder", help="your AppBuilder object")
- def list_users(app, appbuilder):
- """
- List all users on the database
- """
- _appbuilder = import_application(app, appbuilder)
- echo_header("List of users")
- for user in _appbuilder.sm.get_all_users():
- click.echo(
- "username:{0} | email:{1} | role:{2}".format(
- user.username, user.email, user.roles
- )
- )
- @cli_app.command("babel-extract")
- @click.option("--config", default="./babel/babel.cfg")
- @click.option("--input", default=".")
- @click.option("--output", default="./babel/messages.pot")
- @click.option("--target", default="app/translations")
- @click.option(
- "--keywords", "-k", multiple=True, default=["lazy_gettext", "gettext", "_", "__"]
- )
- def babel_extract(config, input, output, target, keywords):
- """
- Babel, Extracts and updates all messages marked for translation
- """
- click.echo(
- click.style(
- "Starting Extractions config:{0} input:{1} output:{2} keywords:{3}".format(
- config, input, output, keywords
- ),
- fg="green",
- )
- )
- keywords = " -k ".join(keywords)
- os.popen(
- "pybabel extract -F {0} -k {1} -o {2} {3}".format(
- config, keywords, output, input
- )
- )
- click.echo(click.style("Starting Update target:{0}".format(target), fg="green"))
- os.popen("pybabel update -N -i {0} -d {1}".format(output, target))
- click.echo(click.style("Finish, you can start your translations", fg="green"))
- @cli_app.command("babel-compile")
- @click.option(
- "--target",
- default="app/translations",
- help="The target directory where translations reside",
- )
- def babel_compile(target):
- """
- Babel, Compiles all translations
- """
- click.echo(click.style("Starting Compile target:{0}".format(target), fg="green"))
- os.popen("pybabel compile -f -d {0}".format(target))
- @cli_app.command("create-app")
- @click.option(
- "--name",
- prompt="Your new app name",
- help="Your application name, directory will have this name",
- )
- @click.option(
- "--engine",
- prompt="Your engine type, SQLAlchemy or MongoEngine",
- type=click.Choice(["SQLAlchemy", "MongoEngine"]),
- default="SQLAlchemy",
- help="Write your engine type",
- )
- def create_app(name, engine):
- """
- Create a Skeleton application (needs internet connection to github)
- """
- try:
- if engine.lower() == "sqlalchemy":
- url = urlopen(SQLA_REPO_URL)
- dirname = "Flask-AppBuilder-Skeleton-master"
- elif engine.lower() == "mongoengine":
- url = urlopen(MONGOENGIE_REPO_URL)
- dirname = "Flask-AppBuilder-Skeleton-me-master"
- zipfile = ZipFile(BytesIO(url.read()))
- zipfile.extractall()
- os.rename(dirname, name)
- click.echo(click.style("Downloaded the skeleton app, good coding!", fg="green"))
- return True
- except Exception as e:
- click.echo(click.style("Something went wrong {0}".format(e), fg="red"))
- if engine.lower() == "sqlalchemy":
- click.echo(
- click.style(
- "Try downloading from {0}".format(SQLA_REPO_URL), fg="green"
- )
- )
- elif engine.lower() == "mongoengine":
- click.echo(
- click.style(
- "Try downloading from {0}".format(MONGOENGIE_REPO_URL), fg="green"
- )
- )
- return False
- @cli_app.command("create-addon")
- @click.option(
- "--name",
- prompt="Your new addon name",
- help="Your addon name will be prefixed by fab_addon_, directory will have this name",
- )
- def create_addon(name):
- """
- Create a Skeleton AddOn (needs internet connection to github)
- """
- try:
- full_name = "fab_addon_" + name
- dirname = "Flask-AppBuilder-Skeleton-AddOn-master"
- url = urlopen(ADDON_REPO_URL)
- zipfile = ZipFile(BytesIO(url.read()))
- zipfile.extractall()
- os.rename(dirname, full_name)
- addon_path = os.path.join(full_name, full_name)
- os.rename(os.path.join(full_name, "fab_addon"), addon_path)
- f = open(os.path.join(full_name, "config.py"), "w")
- f.write("ADDON_NAME='" + name + "'\n")
- f.write("FULL_ADDON_NAME='fab_addon_' + ADDON_NAME\n")
- f.close()
- click.echo(
- click.style("Downloaded the skeleton addon, good coding!", fg="green")
- )
- return True
- except Exception as e:
- click.echo(click.style("Something went wrong {0}".format(e), fg="red"))
- return False
- @cli_app.command("collect-static")
- @click.option(
- "--static_folder", default="app/static", help="Your projects static folder"
- )
- def collect_static(static_folder):
- """
- Copies flask-appbuilder static files to your projects static folder
- """
- appbuilder_static_path = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), "static/appbuilder"
- )
- app_static_path = os.path.join(os.getcwd(), static_folder)
- if not os.path.isdir(app_static_path):
- click.echo(
- click.style(
- "Static folder does not exist creating: %s" % app_static_path,
- fg="green",
- )
- )
- os.makedirs(app_static_path)
- try:
- shutil.copytree(
- appbuilder_static_path, os.path.join(app_static_path, "appbuilder")
- )
- except Exception:
- click.echo(
- click.style(
- "Appbuilder static folder already exists on your project", fg="red"
- )
- )
- def cli():
- cli_app()
- if __name__ == "__main__":
- cli_app()
|