<reponame>tihomirptotev/fapflow
# -*- coding: utf-8 -*-
"""Click commands."""
import os
from glob import glob
from subprocess import call

import click
from flask import current_app
from flask.cli import with_appcontext
from werkzeug.exceptions import MethodNotAllowed, NotFound
from apflow_flask.models.user import User
from apflow_flask.extensions import db
from apflow_flask.data import dev_data

HERE = os.path.abspath(os.path.dirname(__file__))
PROJECT_ROOT = os.path.join(HERE, os.pardir)
TEST_PATH = os.path.join(PROJECT_ROOT, 'tests')


@click.command()
def test():
    """Run the tests."""
    import pytest
    rv = pytest.main([TEST_PATH, '--verbose'])
    exit(rv)


@click.group()
def apflow():
    """ Main admin script """
    pass


@apflow.command()
@click.option('--username', prompt='Enter username')
@click.option('--first_name', prompt='First name')
@click.option('--last_name', prompt='Last name')
@click.option('--email', prompt='Email')
@click.option('--password', prompt='Password', hide_input=True,
              confirmation_prompt=True)
@with_appcontext
def create_admin(username, first_name, last_name, email, password):
    """ Create an admin user. """
    admin = User(username, email, password=password,
                 first_name=first_name, last_name=last_name,
                 active=True, is_admin=True)
    db.session.add(admin)
    db.session.commit()
    click.echo(f'Admin user: {username} created.')


@apflow.command()
@with_appcontext
def initdb_with_test_data():
    """ Create test data """
    db.drop_all()
    db.create_all()
    yaml_data_file = os.path.join(HERE, 'data', 'data.yaml')
    data_manager = dev_data.DevDataImport(db, yaml_data_file)
    data_manager.run_all()


@apflow.command()
@click.confirmation_option(prompt='Are you sure you want to drop the tables?')
@with_appcontext
def drop_all():
    """ Drop all database tables. """
    db.drop_all()
    click.echo('All tables have been dropped!')


@apflow.command()
@with_appcontext
def create_all():
    """ Create all database tables. """
    db.create_all()
    click.echo('All tables have been created!')


@apflow.command()
def add_sample_data():
    """ Add sample data to play with. """
    click.echo('Data added.')


@click.command()
@click.option('-f', '--fix-imports', default=False, is_flag=True,
              help='Fix imports using isort, before linting')
def lint(fix_imports):
    """Lint and check code style with flake8 and isort."""
    skip = ['node_modules', 'requirements']
    root_files = glob('*.py')
    root_directories = [
        name for name in next(os.walk('.'))[1] if not name.startswith('.')]
    files_and_directories = [
        arg for arg in root_files + root_directories if arg not in skip]

    def execute_tool(description, *args):
        """Execute a checking tool with its arguments."""
        command_line = list(args) + files_and_directories
        click.echo('{}: {}'.format(description, ' '.join(command_line)))
        rv = call(command_line)
        if rv != 0:
            exit(rv)

    if fix_imports:
        execute_tool('Fixing import order', 'isort', '-rc')
    execute_tool('Checking code style', 'flake8')


@click.command()
def clean():
    """Remove *.pyc and *.pyo files recursively starting at current directory.

    Borrowed from Flask-Script, converted to use Click.
    """
    for dirpath, dirnames, filenames in os.walk('.'):
        for filename in filenames:
            if filename.endswith('.pyc') or filename.endswith('.pyo'):
                full_pathname = os.path.join(dirpath, filename)
                click.echo('Removing {}'.format(full_pathname))
                os.remove(full_pathname)


@click.command()
@click.option('--url', default=None,
              help='Url to test (ex. /static/image.png)')
@click.option('--order', default='rule',
              help='Property on Rule to order by (default: rule)')
@with_appcontext
def urls(url, order):
    """Display all of the url matching routes for the project.

    Borrowed from Flask-Script, converted to use Click.
    """
    rows = []
    column_length = 0
    column_headers = ('Rule', 'Endpoint', 'Arguments')

    if url:
        try:
            rule, arguments = (
                current_app.url_map
                           .bind('localhost')
                           .match(url, return_rule=True))
            rows.append((rule.rule, rule.endpoint, arguments))
            column_length = 3
        except (NotFound, MethodNotAllowed) as e:
            rows.append(('<{}>'.format(e), None, None))
            column_length = 1
    else:
        rules = sorted(
            current_app.url_map.iter_rules(),
            key=lambda rule: getattr(rule, order))
        for rule in rules:
            rows.append((rule.rule, rule.endpoint, None))
        column_length = 2

    str_template = ''
    table_width = 0

    if column_length >= 1:
        max_rule_length = max(len(r[0]) for r in rows)
        max_rule_length = max_rule_length if max_rule_length > 4 else 4
        str_template += '{:' + str(max_rule_length) + '}'
        table_width += max_rule_length

    if column_length >= 2:
        max_endpoint_length = max(len(str(r[1])) for r in rows)
        # max_endpoint_length = max(rows, key=len)
        max_endpoint_length = (
            max_endpoint_length if max_endpoint_length > 8 else 8)
        str_template += '  {:' + str(max_endpoint_length) + '}'
        table_width += 2 + max_endpoint_length

    if column_length >= 3:
        max_arguments_length = max(len(str(r[2])) for r in rows)
        max_arguments_length = (
            max_arguments_length if max_arguments_length > 9 else 9)
        str_template += '  {:' + str(max_arguments_length) + '}'
        table_width += 2 + max_arguments_length

    click.echo(str_template.format(*column_headers[:column_length]))
    click.echo('-' * table_width)

    for row in rows:
        click.echo(str_template.format(*row[:column_length]))

# -*- coding: utf-8 -*-
"""Click commands."""
import os
from glob import glob
from subprocess import call

import click
from flask import current_app
from flask.cli import with_appcontext
from werkzeug.exceptions import MethodNotAllowed, NotFound
from apflow_flask.models.user import User
from apflow_flask.extensions import db
from apflow_flask.data import dev_data

HERE = os.path.abspath(os.path.dirname(__file__))
PROJECT_ROOT = os.path.join(HERE, os.pardir)
TEST_PATH = os.path.join(PROJECT_ROOT, 'tests')


@click.command()
def test():
    """Run the tests."""
    import pytest
    rv = pytest.main([TEST_PATH, '--verbose'])
    exit(rv)


@click.group()
def apflow():
    """ Main admin script """
    pass


@apflow.command()
@click.option('--username', prompt='Enter username')
@click.option('--first_name', prompt='First name')
@click.option('--last_name', prompt='Last name')
@click.option('--email', prompt='Email')
@click.option('--password', prompt='Password', hide_input=True,
              confirmation_prompt=True)
@with_appcontext
def create_admin(username, first_name, last_name, email, password):
    """ Create an admin user. """
    admin = User(username, email, password=password,
                 first_name=first_name, last_name=last_name,
                 active=True, is_admin=True)
    db.session.add(admin)
    db.session.commit()
    click.echo(f'Admin user: {username} created.')


@apflow.command()
@with_appcontext
def initdb_with_test_data():
    """ Create test data """
    db.drop_all()
    db.create_all()
    yaml_data_file = os.path.join(HERE, 'data', 'data.yaml')
    data_manager = dev_data.DevDataImport(db, yaml_data_file)
    data_manager.run_all()


@apflow.command()
@click.confirmation_option(prompt='Are you sure you want to drop the tables?')
@with_appcontext
def drop_all():
    """ Drop all database tables. """
    db.drop_all()
    click.echo('All tables have been dropped!')


@apflow.command()
@with_appcontext
def create_all():
    """ Create all database tables. """
    db.create_all()
    click.echo('All tables have been created!')


@apflow.command()
def add_sample_data():
    """ Add sample data to play with. """
    click.echo('Data added.')


@click.command()
@click.option('-f', '--fix-imports', default=False, is_flag=True,
              help='Fix imports using isort, before linting')
def lint(fix_imports):
    """Lint and check code style with flake8 and isort."""
    skip = ['node_modules', 'requirements']
    root_files = glob('*.py')
    root_directories = [
        name for name in next(os.walk('.'))[1] if not name.startswith('.')]
    files_and_directories = [
        arg for arg in root_files + root_directories if arg not in skip]

    def execute_tool(description, *args):
        """Execute a checking tool with its arguments."""
        command_line = list(args) + files_and_directories
        click.echo('{}: {}'.format(description, ' '.join(command_line)))
        rv = call(command_line)
        if rv != 0:
            exit(rv)

    if fix_imports:
        execute_tool('Fixing import order', 'isort', '-rc')
    execute_tool('Checking code style', 'flake8')


@click.command()
def clean():
    """Remove *.pyc and *.pyo files recursively starting at current directory.

    Borrowed from Flask-Script, converted to use Click.
    """
    for dirpath, dirnames, filenames in os.walk('.'):
        for filename in filenames:
            if filename.endswith('.pyc') or filename.endswith('.pyo'):
                full_pathname = os.path.join(dirpath, filename)
                click.echo('Removing {}'.format(full_pathname))
                os.remove(full_pathname)


@click.command()
@click.option('--url', default=None,
              help='Url to test (ex. /static/image.png)')
@click.option('--order', default='rule',
              help='Property on Rule to order by (default: rule)')
@with_appcontext
def urls(url, order):
    """Display all of the url matching routes for the project.

    Borrowed from Flask-Script, converted to use Click.
    """
    rows = []
    column_length = 0
    column_headers = ('Rule', 'Endpoint', 'Arguments')

    if url:
        try:
            rule, arguments = (
                current_app.url_map
                           .bind('localhost')
                           .match(url, return_rule=True))
            rows.append((rule.rule, rule.endpoint, arguments))
            column_length = 3
        except (NotFound, MethodNotAllowed) as e:
            rows.append(('<{}>'.format(e), None, None))
            column_length = 1
    else:
        rules = sorted(
            current_app.url_map.iter_rules(),
            key=lambda rule: getattr(rule, order))
        for rule in rules:
            rows.append((rule.rule, rule.endpoint, None))
        column_length = 2

    str_template = ''
    table_width = 0

    if column_length >= 1:
        max_rule_length = max(len(r[0]) for r in rows)
        max_rule_length = max_rule_length if max_rule_length > 4 else 4
        str_template += '{:' + str(max_rule_length) + '}'
        table_width += max_rule_length

    if column_length >= 2:
        max_endpoint_length = max(len(str(r[1])) for r in rows)
        # max_endpoint_length = max(rows, key=len)
        max_endpoint_length = (
            max_endpoint_length if max_endpoint_length > 8 else 8)
        str_template += '  {:' + str(max_endpoint_length) + '}'
        table_width += 2 + max_endpoint_length

    if column_length >= 3:
        max_arguments_length = max(len(str(r[2])) for r in rows)
        max_arguments_length = (
            max_arguments_length if max_arguments_length > 9 else 9)
        str_template += '  {:' + str(max_arguments_length) + '}'
        table_width += 2 + max_arguments_length

    click.echo(str_template.format(*column_headers[:column_length]))
    click.echo('-' * table_width)

    for row in rows:
        click.echo(str_template.format(*row[:column_length]))
# -*- coding: utf-8 -*- Click commands. Run the tests. Main admin script Create an admin user. Create test data Drop all database tables. Create all database tables. Add sample data to play with. Lint and check code style with flake8 and isort. Execute a checking tool with its arguments. Remove *.pyc and *.pyo files recursively starting at current directory.      Borrowed from Flask-Script, converted to use Click. Display all of the url matching routes for the project.      Borrowed from Flask-Script, converted to use Click. # max_endpoint_length = max(rows, key=len)