splent-cli 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splent_cli/__init__.py +6 -0
- splent_cli/__main__.py +11 -0
- splent_cli/cli.py +59 -0
- splent_cli/commands/__init__.py +0 -0
- splent_cli/commands/clear_cache.py +90 -0
- splent_cli/commands/clear_log.py +31 -0
- splent_cli/commands/clear_uploads.py +44 -0
- splent_cli/commands/compose_env.py +50 -0
- splent_cli/commands/coverage.py +46 -0
- splent_cli/commands/db_console.py +27 -0
- splent_cli/commands/db_dump.py +45 -0
- splent_cli/commands/db_migrate.py +37 -0
- splent_cli/commands/db_reset.py +90 -0
- splent_cli/commands/db_seed.py +123 -0
- splent_cli/commands/env.py +18 -0
- splent_cli/commands/info.py +74 -0
- splent_cli/commands/linter.py +146 -0
- splent_cli/commands/locust.py +219 -0
- splent_cli/commands/module_create.py +138 -0
- splent_cli/commands/module_list.py +28 -0
- splent_cli/commands/route_list.py +68 -0
- splent_cli/commands/selenium.py +85 -0
- splent_cli/commands/test.py +45 -0
- splent_cli/commands/update.py +159 -0
- splent_cli/commands/webpack_compile.py +68 -0
- splent_cli/utils/__init__.py +0 -0
- splent_cli/utils/path_utils.py +116 -0
- splent_cli-0.0.1.dist-info/LICENSE +113 -0
- splent_cli-0.0.1.dist-info/METADATA +133 -0
- splent_cli-0.0.1.dist-info/RECORD +33 -0
- splent_cli-0.0.1.dist-info/WHEEL +5 -0
- splent_cli-0.0.1.dist-info/entry_points.txt +2 -0
- splent_cli-0.0.1.dist-info/top_level.txt +1 -0
splent_cli/__init__.py
ADDED
splent_cli/__main__.py
ADDED
splent_cli/cli.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import importlib
|
|
4
|
+
import click
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
from flask.cli import FlaskGroup # 📌 Añadir FlaskGroup
|
|
7
|
+
from splent_app import create_app # 📌 Importar la app de Flask
|
|
8
|
+
|
|
9
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
10
|
+
|
|
11
|
+
load_dotenv()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def check_working_dir():
|
|
15
|
+
working_dir = os.getenv("WORKING_DIR", "").strip()
|
|
16
|
+
|
|
17
|
+
if not working_dir:
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
if working_dir in ["/app", "/vagrant", "/app/", "/vagrant/"] and not os.path.exists(working_dir):
|
|
21
|
+
print(f"⚠️ WARNING: WORKING_DIR is set to '{working_dir}', but the directory does not exist.")
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SPLENTCLI(FlaskGroup): # 📌 Usamos FlaskGroup para conectar con Flask
|
|
26
|
+
def __init__(self, **kwargs):
|
|
27
|
+
super().__init__(create_app=create_app, **kwargs)
|
|
28
|
+
|
|
29
|
+
def get_command(self, ctx, cmd_name):
|
|
30
|
+
rv = super().get_command(ctx, cmd_name)
|
|
31
|
+
if rv is None:
|
|
32
|
+
click.echo(f"No such command '{cmd_name}'.")
|
|
33
|
+
click.echo("Try 'splent_cli --help' for a list of available commands.")
|
|
34
|
+
return rv
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_commands(cli_group, commands_dir="splent_cli/commands"):
|
|
38
|
+
"""
|
|
39
|
+
Dynamically import all commands in the specified directory and add them to the CLI group.
|
|
40
|
+
"""
|
|
41
|
+
commands_path = PathUtils.get_commands_path()
|
|
42
|
+
|
|
43
|
+
for file in os.listdir(commands_path):
|
|
44
|
+
if file.endswith(".py") and not file.startswith("__"):
|
|
45
|
+
module_name = f"splent_cli.commands.{file[:-3]}"
|
|
46
|
+
module = importlib.import_module(module_name)
|
|
47
|
+
for attr_name in dir(module):
|
|
48
|
+
attr = getattr(module, attr_name)
|
|
49
|
+
if isinstance(attr, click.Command):
|
|
50
|
+
cli_group.add_command(attr)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@click.group(cls=SPLENTCLI)
|
|
54
|
+
def cli():
|
|
55
|
+
"""A CLI tool to help with project development."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
cli()
|
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import shutil
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command(
|
|
11
|
+
"clear:cache",
|
|
12
|
+
help="Clears pytest cache in app/modules and the build directory at the root.",
|
|
13
|
+
)
|
|
14
|
+
def clear_cache():
|
|
15
|
+
|
|
16
|
+
if click.confirm(
|
|
17
|
+
"Are you sure you want to clear the pytest cache and the build directory?"
|
|
18
|
+
):
|
|
19
|
+
|
|
20
|
+
project_root = Path(os.getenv("WORKING_DIR", ""))
|
|
21
|
+
|
|
22
|
+
pytest_cache_dir = os.path.join(
|
|
23
|
+
PathUtils.get_modules_dir(), ".pytest_cache"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
build_dir = os.path.join(os.getenv("WORKING_DIR", ""), "build")
|
|
27
|
+
|
|
28
|
+
if os.path.exists(pytest_cache_dir):
|
|
29
|
+
try:
|
|
30
|
+
shutil.rmtree(pytest_cache_dir)
|
|
31
|
+
click.echo(click.style("Pytest cache cleared.", fg="green"))
|
|
32
|
+
except Exception as e:
|
|
33
|
+
click.echo(
|
|
34
|
+
click.style(f"Failed to clear pytest cache: {e}", fg="red")
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
click.echo(
|
|
38
|
+
click.style(
|
|
39
|
+
"No pytest cache found. Nothing to clear.", fg="yellow"
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if os.path.exists(build_dir):
|
|
44
|
+
try:
|
|
45
|
+
shutil.rmtree(build_dir)
|
|
46
|
+
click.echo(click.style("Build directory cleared.", fg="green"))
|
|
47
|
+
except Exception as e:
|
|
48
|
+
click.echo(
|
|
49
|
+
click.style(
|
|
50
|
+
f"Failed to clear build directory: {e}", fg="red"
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
click.echo(
|
|
55
|
+
click.style(
|
|
56
|
+
"No cache or build directory found. Nothing to clear.",
|
|
57
|
+
fg="yellow",
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
pycache_dirs = project_root.rglob("__pycache__")
|
|
62
|
+
for dir in pycache_dirs:
|
|
63
|
+
try:
|
|
64
|
+
shutil.rmtree(dir)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
click.echo(
|
|
67
|
+
click.style(
|
|
68
|
+
f"Failed to clear __pycache__ directory {dir}: {e}",
|
|
69
|
+
fg="red",
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
click.echo(
|
|
73
|
+
click.style("All __pycache__ directories cleared.", fg="green")
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
pyc_files = project_root.rglob("*.pyc")
|
|
77
|
+
for file in pyc_files:
|
|
78
|
+
try:
|
|
79
|
+
file.unlink()
|
|
80
|
+
except Exception as e:
|
|
81
|
+
click.echo(
|
|
82
|
+
click.style(
|
|
83
|
+
f"Failed to clear .pyc file {file}: {e}", fg="red"
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
click.echo(click.style("All cache cleared.", fg="green"))
|
|
88
|
+
|
|
89
|
+
else:
|
|
90
|
+
click.echo(click.style("Clear operation cancelled.", fg="yellow"))
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command("clear:log", help="Clears the 'app.log' file.")
|
|
8
|
+
def clear_log():
|
|
9
|
+
log_file_path = PathUtils.get_app_log_dir()
|
|
10
|
+
|
|
11
|
+
# Check if the log file exists
|
|
12
|
+
if os.path.exists(log_file_path):
|
|
13
|
+
try:
|
|
14
|
+
# Deletes the log file
|
|
15
|
+
os.remove(log_file_path)
|
|
16
|
+
click.echo(
|
|
17
|
+
click.style(
|
|
18
|
+
"The 'app.log' file has been successfully cleared.",
|
|
19
|
+
fg="green",
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
except Exception as e:
|
|
23
|
+
click.echo(
|
|
24
|
+
click.style(
|
|
25
|
+
f"Error clearing the 'app.log' file: {e}", fg="red"
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
else:
|
|
29
|
+
click.echo(
|
|
30
|
+
click.style("The 'app.log' file does not exist.", fg="yellow")
|
|
31
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import shutil
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command(
|
|
9
|
+
"clear:uploads",
|
|
10
|
+
help="Clears the contents of the 'uploads' directory without removing the folder.",
|
|
11
|
+
)
|
|
12
|
+
def clear_uploads():
|
|
13
|
+
uploads_dir = PathUtils.get_uploads_dir()
|
|
14
|
+
|
|
15
|
+
# Verify if the 'uploads' folder exists
|
|
16
|
+
if os.path.exists(uploads_dir) and os.path.isdir(uploads_dir):
|
|
17
|
+
try:
|
|
18
|
+
# Iterate over the contents of the directory
|
|
19
|
+
for filename in os.listdir(uploads_dir):
|
|
20
|
+
file_path = os.path.join(uploads_dir, filename)
|
|
21
|
+
|
|
22
|
+
# If it's a file, remove it
|
|
23
|
+
if os.path.isfile(file_path) or os.path.islink(file_path):
|
|
24
|
+
os.remove(file_path)
|
|
25
|
+
# If it's a directory, remove it and its contents
|
|
26
|
+
elif os.path.isdir(file_path):
|
|
27
|
+
shutil.rmtree(file_path)
|
|
28
|
+
|
|
29
|
+
click.echo(
|
|
30
|
+
click.style(
|
|
31
|
+
"The contents of the 'uploads' directory have been successfully cleared.",
|
|
32
|
+
fg="green",
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
click.echo(
|
|
37
|
+
click.style(
|
|
38
|
+
f"Error clearing the 'uploads' directory: {e}", fg="red"
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
else:
|
|
42
|
+
click.echo(
|
|
43
|
+
click.style("The 'uploads' directory does not exist.", fg="yellow")
|
|
44
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
from dotenv import dotenv_values
|
|
4
|
+
from flask.cli import with_appcontext
|
|
5
|
+
|
|
6
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command(
|
|
10
|
+
"compose:env",
|
|
11
|
+
help="Combines .env files from blueprints with the root .env, checking for conflicts.",
|
|
12
|
+
)
|
|
13
|
+
@with_appcontext
|
|
14
|
+
def compose_env():
|
|
15
|
+
|
|
16
|
+
modules_dir = PathUtils.get_modules_dir()
|
|
17
|
+
root_env_path = PathUtils.get_env_dir()
|
|
18
|
+
|
|
19
|
+
# Loads the current root .env variables into a dictionary
|
|
20
|
+
root_env_vars = dotenv_values(root_env_path)
|
|
21
|
+
|
|
22
|
+
# Finds and processes all blueprints .env files
|
|
23
|
+
module_env_paths = [
|
|
24
|
+
os.path.join(root, ".env")
|
|
25
|
+
for root, dirs, files in os.walk(modules_dir)
|
|
26
|
+
if ".env" in files
|
|
27
|
+
]
|
|
28
|
+
for env_path in module_env_paths:
|
|
29
|
+
blueprint_env_vars = dotenv_values(env_path)
|
|
30
|
+
# Add or update the blueprint variables in the root .env dictionary
|
|
31
|
+
for key, value in blueprint_env_vars.items():
|
|
32
|
+
if key in root_env_vars and root_env_vars[key] != value:
|
|
33
|
+
conflict_msg = (
|
|
34
|
+
f"Conflict found for variable '{key}' in {env_path}. "
|
|
35
|
+
"Keeping the original value."
|
|
36
|
+
)
|
|
37
|
+
click.echo(click.style(conflict_msg, fg="yellow"))
|
|
38
|
+
continue
|
|
39
|
+
root_env_vars[key] = value
|
|
40
|
+
|
|
41
|
+
# Write back to the root .env file
|
|
42
|
+
with open(root_env_path, "w") as root_env_file:
|
|
43
|
+
for key, value in root_env_vars.items():
|
|
44
|
+
root_env_file.write(f"{key}={value}\n")
|
|
45
|
+
|
|
46
|
+
click.echo(
|
|
47
|
+
click.style(
|
|
48
|
+
"Successfully merged .env files without conflicts.", fg="green"
|
|
49
|
+
)
|
|
50
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import subprocess
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command(
|
|
9
|
+
"coverage",
|
|
10
|
+
help="Runs pytest coverage on the blueprints directory or a specific module.",
|
|
11
|
+
)
|
|
12
|
+
@click.argument("module_name", required=False)
|
|
13
|
+
@click.option(
|
|
14
|
+
"--html", is_flag=True, help="Generates an HTML coverage report."
|
|
15
|
+
)
|
|
16
|
+
def coverage(module_name, html):
|
|
17
|
+
modules_dir = PathUtils.get_modules_dir()
|
|
18
|
+
test_path = modules_dir
|
|
19
|
+
|
|
20
|
+
if module_name:
|
|
21
|
+
test_path = os.path.join(modules_dir, module_name)
|
|
22
|
+
if not os.path.exists(test_path):
|
|
23
|
+
click.echo(
|
|
24
|
+
click.style(
|
|
25
|
+
f"Module '{module_name}' does not exist.", fg="red"
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
return
|
|
29
|
+
click.echo(f"Running coverage for the '{module_name}' module...")
|
|
30
|
+
else:
|
|
31
|
+
click.echo("Running coverage for all modules...")
|
|
32
|
+
|
|
33
|
+
coverage_cmd = [
|
|
34
|
+
"pytest",
|
|
35
|
+
"--ignore-glob=*selenium*",
|
|
36
|
+
"--cov=" + test_path,
|
|
37
|
+
test_path,
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
if html:
|
|
41
|
+
coverage_cmd.extend(["--cov-report", "html"])
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
subprocess.run(coverage_cmd, check=True)
|
|
45
|
+
except subprocess.CalledProcessError as e:
|
|
46
|
+
click.echo(click.style(f"Error running coverage: {e}", fg="red"))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import subprocess
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command(
|
|
8
|
+
"db:console", help="Opens a MariaDB console with credentials from .env."
|
|
9
|
+
)
|
|
10
|
+
def db_console():
|
|
11
|
+
load_dotenv()
|
|
12
|
+
|
|
13
|
+
mariadb_hostname = os.getenv("MARIADB_HOSTNAME")
|
|
14
|
+
mariadb_user = os.getenv("MARIADB_USER")
|
|
15
|
+
mariadb_password = os.getenv("MARIADB_PASSWORD")
|
|
16
|
+
mariadb_database = os.getenv("MARIADB_DATABASE")
|
|
17
|
+
|
|
18
|
+
# Build the command to connect to MariaDB
|
|
19
|
+
mariadb_connect_cmd = f"mysql -h{mariadb_hostname} -u{mariadb_user} -p{mariadb_password} {mariadb_database}"
|
|
20
|
+
|
|
21
|
+
# Execute the command
|
|
22
|
+
try:
|
|
23
|
+
subprocess.run(mariadb_connect_cmd, shell=True, check=True)
|
|
24
|
+
except subprocess.CalledProcessError as e:
|
|
25
|
+
click.echo(
|
|
26
|
+
click.style(f"Error opening MariaDB console: {e}", fg="red")
|
|
27
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import subprocess
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
import os
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command(
|
|
9
|
+
"db:dump",
|
|
10
|
+
help="Creates a dump of the MariaDB database with credentials from .env.",
|
|
11
|
+
)
|
|
12
|
+
@click.argument("filename", required=False)
|
|
13
|
+
def db_dump(filename):
|
|
14
|
+
load_dotenv()
|
|
15
|
+
|
|
16
|
+
mariadb_hostname = os.getenv("MARIADB_HOSTNAME")
|
|
17
|
+
mariadb_user = os.getenv("MARIADB_USER")
|
|
18
|
+
mariadb_password = os.getenv("MARIADB_PASSWORD")
|
|
19
|
+
mariadb_database = os.getenv("MARIADB_DATABASE")
|
|
20
|
+
|
|
21
|
+
# Generate default filename if not provided
|
|
22
|
+
if not filename:
|
|
23
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
24
|
+
filename = f"dump_{timestamp}.sql"
|
|
25
|
+
else:
|
|
26
|
+
# Ensure filename has .sql extension
|
|
27
|
+
if not filename.endswith(".sql"):
|
|
28
|
+
filename += ".sql"
|
|
29
|
+
|
|
30
|
+
# Build the mysqldump command
|
|
31
|
+
dump_cmd = f"mysqldump -h{mariadb_hostname} -u{mariadb_user} -p{mariadb_password} \
|
|
32
|
+
{mariadb_database} > {filename}"
|
|
33
|
+
|
|
34
|
+
# Execute the command
|
|
35
|
+
try:
|
|
36
|
+
subprocess.run(
|
|
37
|
+
dump_cmd, shell=True, check=True, executable="/bin/bash"
|
|
38
|
+
)
|
|
39
|
+
click.echo(
|
|
40
|
+
click.style(
|
|
41
|
+
f"Database dump created successfully: {filename}", fg="green"
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
except subprocess.CalledProcessError as e:
|
|
45
|
+
click.echo(click.style(f"Error creating database dump: {e}", fg="red"))
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import click
|
|
3
|
+
from flask.cli import with_appcontext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command("db:migrate", help="Generates and applies database migrations.")
|
|
7
|
+
@with_appcontext
|
|
8
|
+
def db_migrate():
|
|
9
|
+
# Generates migrations
|
|
10
|
+
click.echo("Generating database migrations...")
|
|
11
|
+
result_migrate = subprocess.run(["flask", "db", "migrate"])
|
|
12
|
+
if result_migrate.returncode == 0:
|
|
13
|
+
click.echo(
|
|
14
|
+
click.style("Migrations generated successfully.", fg="green")
|
|
15
|
+
)
|
|
16
|
+
else:
|
|
17
|
+
click.echo(
|
|
18
|
+
click.style(
|
|
19
|
+
"Note: No new migrations needed or an error occurred "
|
|
20
|
+
"while generating migrations.",
|
|
21
|
+
fg="yellow",
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Applies to migrations
|
|
26
|
+
click.echo("Applying database migrations...")
|
|
27
|
+
result_upgrade = subprocess.run(["flask", "db", "upgrade"])
|
|
28
|
+
if result_upgrade.returncode == 0:
|
|
29
|
+
click.echo(click.style("Migrations applied successfully.", fg="green"))
|
|
30
|
+
else:
|
|
31
|
+
click.echo(
|
|
32
|
+
click.style(
|
|
33
|
+
"Error applying migrations. This may be due to the database "
|
|
34
|
+
"being already up-to-date.",
|
|
35
|
+
fg="yellow",
|
|
36
|
+
)
|
|
37
|
+
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import shutil
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from flask.cli import with_appcontext
|
|
6
|
+
from splent_app import create_app, db
|
|
7
|
+
from sqlalchemy import MetaData
|
|
8
|
+
|
|
9
|
+
from splent_cli.commands.clear_uploads import clear_uploads
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command(
|
|
13
|
+
"db:reset",
|
|
14
|
+
help="Resets the database, optionally clears migrations and recreates them.",
|
|
15
|
+
)
|
|
16
|
+
@click.option(
|
|
17
|
+
"--clear-migrations",
|
|
18
|
+
is_flag=True,
|
|
19
|
+
help="Remove all tables including 'alembic_version', clear migrations folder, and recreate migrations.",
|
|
20
|
+
)
|
|
21
|
+
@click.option(
|
|
22
|
+
"-y",
|
|
23
|
+
"--yes",
|
|
24
|
+
is_flag=True,
|
|
25
|
+
help="Confirm the operation without prompting.",
|
|
26
|
+
)
|
|
27
|
+
@with_appcontext
|
|
28
|
+
def db_reset(clear_migrations, yes):
|
|
29
|
+
app = create_app()
|
|
30
|
+
with app.app_context():
|
|
31
|
+
if not yes and not click.confirm(
|
|
32
|
+
"WARNING: This will delete all data and clear uploads. Are you sure?",
|
|
33
|
+
abort=True,
|
|
34
|
+
):
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# Deletes data from all tables
|
|
38
|
+
try:
|
|
39
|
+
meta = MetaData()
|
|
40
|
+
meta.reflect(bind=db.engine)
|
|
41
|
+
with db.engine.connect() as conn:
|
|
42
|
+
trans = conn.begin() # Begin transaction
|
|
43
|
+
for table in reversed(meta.sorted_tables):
|
|
44
|
+
if not clear_migrations or table.name != "alembic_version":
|
|
45
|
+
conn.execute(table.delete())
|
|
46
|
+
trans.commit() # End transaction
|
|
47
|
+
click.echo(click.style("All table data cleared.", fg="yellow"))
|
|
48
|
+
subprocess.run(["flask", "db", "stamp", "head"], check=True)
|
|
49
|
+
except Exception as e:
|
|
50
|
+
click.echo(
|
|
51
|
+
click.style(f"Error clearing table data: {e}", fg="red")
|
|
52
|
+
)
|
|
53
|
+
if trans:
|
|
54
|
+
trans.rollback()
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
# Delete the uploads folder
|
|
58
|
+
ctx = click.get_current_context()
|
|
59
|
+
ctx.invoke(clear_uploads)
|
|
60
|
+
|
|
61
|
+
if clear_migrations:
|
|
62
|
+
# Delete the migration folder if it exists.
|
|
63
|
+
migrations_dir = os.path.join(
|
|
64
|
+
os.getenv("WORKING_DIR", ""), "migrations"
|
|
65
|
+
)
|
|
66
|
+
if os.path.isdir(migrations_dir):
|
|
67
|
+
shutil.rmtree(migrations_dir)
|
|
68
|
+
click.echo(
|
|
69
|
+
click.style("Migrations directory cleared.", fg="yellow")
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Run flask db init, migrate and upgrade
|
|
73
|
+
try:
|
|
74
|
+
subprocess.run(["flask", "db", "init"], check=True)
|
|
75
|
+
subprocess.run(["flask", "db", "migrate"], check=True)
|
|
76
|
+
subprocess.run(["flask", "db", "upgrade"], check=True)
|
|
77
|
+
click.echo(
|
|
78
|
+
click.style(
|
|
79
|
+
"Database recreated from new migrations.", fg="green"
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
except subprocess.CalledProcessError as e:
|
|
83
|
+
click.echo(
|
|
84
|
+
click.style(
|
|
85
|
+
f"Error during migrations reset: {e}", fg="red"
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
click.echo(click.style("Database reset successfully.", fg="green"))
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import os
|
|
3
|
+
import importlib
|
|
4
|
+
import click
|
|
5
|
+
from flask.cli import with_appcontext
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from splent_framework.core.seeders import BaseSeeder
|
|
9
|
+
from splent_cli.commands.db_reset import db_reset
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_module_seeders(module_path, specific_module=None):
|
|
13
|
+
seeders = []
|
|
14
|
+
for root, dirs, files in os.walk(module_path):
|
|
15
|
+
if "seeders.py" in files:
|
|
16
|
+
relative_path = os.path.relpath(root, module_path)
|
|
17
|
+
module_name = relative_path.replace(os.path.sep, ".")
|
|
18
|
+
full_module_name = f"app.modules.{module_name}.seeders"
|
|
19
|
+
|
|
20
|
+
# If a module was specified and does not match the current one, continue with the next one
|
|
21
|
+
if (
|
|
22
|
+
specific_module
|
|
23
|
+
and specific_module != module_name.split(".")[0]
|
|
24
|
+
):
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
seeder_module = importlib.import_module(full_module_name)
|
|
28
|
+
importlib.reload(seeder_module) # Reload the module
|
|
29
|
+
|
|
30
|
+
for attr in dir(seeder_module):
|
|
31
|
+
potential_seeder_class = getattr(seeder_module, attr)
|
|
32
|
+
if (
|
|
33
|
+
inspect.isclass(potential_seeder_class)
|
|
34
|
+
and issubclass(potential_seeder_class, BaseSeeder)
|
|
35
|
+
and potential_seeder_class is not BaseSeeder
|
|
36
|
+
):
|
|
37
|
+
seeders.append(potential_seeder_class())
|
|
38
|
+
|
|
39
|
+
# Sort seeders by priority
|
|
40
|
+
seeders.sort(key=lambda seeder: seeder.priority)
|
|
41
|
+
|
|
42
|
+
return seeders
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@click.command(
|
|
46
|
+
"db:seed",
|
|
47
|
+
help="Populates the database with the seeders defined in each module.",
|
|
48
|
+
)
|
|
49
|
+
@click.option(
|
|
50
|
+
"--reset", is_flag=True, help="Reset the database before seeding."
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"-y",
|
|
54
|
+
"--yes",
|
|
55
|
+
is_flag=True,
|
|
56
|
+
help="Confirm the operation without prompting.",
|
|
57
|
+
)
|
|
58
|
+
@click.argument("module", required=False)
|
|
59
|
+
@with_appcontext
|
|
60
|
+
def db_seed(reset, yes, module):
|
|
61
|
+
|
|
62
|
+
if reset:
|
|
63
|
+
if yes or click.confirm(
|
|
64
|
+
click.style(
|
|
65
|
+
"This will reset the database, do you want " "to continue?",
|
|
66
|
+
fg="red",
|
|
67
|
+
),
|
|
68
|
+
abort=True,
|
|
69
|
+
):
|
|
70
|
+
click.echo(click.style("Resetting the database...", fg="yellow"))
|
|
71
|
+
ctx = click.get_current_context()
|
|
72
|
+
ctx.invoke(db_reset, clear_migrations=False, yes=True)
|
|
73
|
+
else:
|
|
74
|
+
click.echo(click.style("Database reset cancelled.", fg="yellow"))
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
blueprints_module_path = os.path.join(
|
|
78
|
+
os.getenv("WORKING_DIR", ""), "app/modules"
|
|
79
|
+
)
|
|
80
|
+
seeders = get_module_seeders(
|
|
81
|
+
blueprints_module_path, specific_module=module
|
|
82
|
+
)
|
|
83
|
+
success = True # Flag to control the successful flow of the operation
|
|
84
|
+
|
|
85
|
+
if module:
|
|
86
|
+
click.echo(
|
|
87
|
+
click.style(
|
|
88
|
+
f"Seeding data for the '{module}' module...", fg="green"
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
click.echo(click.style("Seeding data for all modules...", fg="green"))
|
|
93
|
+
|
|
94
|
+
for seeder in seeders:
|
|
95
|
+
try:
|
|
96
|
+
seeder.run()
|
|
97
|
+
click.echo(
|
|
98
|
+
click.style(
|
|
99
|
+
f"{seeder.__class__.__name__} performed.", fg="blue"
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
click.echo(
|
|
104
|
+
click.style(
|
|
105
|
+
f"Error running seeder {seeder.__class__.__name__}: {e}",
|
|
106
|
+
fg="red",
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
click.echo(
|
|
110
|
+
click.style(
|
|
111
|
+
f"Rolled back the transaction of {seeder.__class__.__name__} to keep the session "
|
|
112
|
+
f"clean.",
|
|
113
|
+
fg="yellow",
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
success = False
|
|
118
|
+
break
|
|
119
|
+
|
|
120
|
+
if success:
|
|
121
|
+
click.echo(
|
|
122
|
+
click.style("Database populated with test data.", fg="green")
|
|
123
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# splent_cli/commands/env.py
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from dotenv import dotenv_values
|
|
5
|
+
|
|
6
|
+
from splent_cli.utils.path_utils import PathUtils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
def env():
|
|
11
|
+
"""Displays the current .env file values."""
|
|
12
|
+
# Load the .env file
|
|
13
|
+
env_dir = PathUtils.get_env_dir()
|
|
14
|
+
env_values = dotenv_values(env_dir)
|
|
15
|
+
|
|
16
|
+
# Display keys and values
|
|
17
|
+
for key, value in env_values.items():
|
|
18
|
+
click.echo(f"{key}={value}")
|