flask-commands 0.1.0__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.
- flask_commands/__init__.py +1 -0
- flask_commands/cli.py +12 -0
- flask_commands/commands/controller.py +0 -0
- flask_commands/commands/form.py +0 -0
- flask_commands/commands/model.py +0 -0
- flask_commands/commands/new.py +60 -0
- flask_commands/commands/route.py +0 -0
- flask_commands/commands/view.py +171 -0
- flask_commands/project/.env +6 -0
- flask_commands/project/.env.example +6 -0
- flask_commands/project/app/__init__.py +31 -0
- flask_commands/project/app/controllers/__init__.py +1 -0
- flask_commands/project/app/controllers/main_controller.py +6 -0
- flask_commands/project/app/models/__init__.py +1 -0
- flask_commands/project/app/models/user.py +51 -0
- flask_commands/project/app/routes/mains/__init__.py +5 -0
- flask_commands/project/app/routes/mains/routes.py +6 -0
- flask_commands/project/app/static/src/input.css +1 -0
- flask_commands/project/app/templates/mains/index.html +14 -0
- flask_commands/project/config/__init__.py +8 -0
- flask_commands/project/config/base_config.py +12 -0
- flask_commands/project/config/development_config.py +8 -0
- flask_commands/project/config/production_config.py +7 -0
- flask_commands/project/run.py +62 -0
- flask_commands/project/run.sh +64 -0
- flask_commands/utils/__init__.py +1 -0
- flask_commands/utils/controllers.py +148 -0
- flask_commands/utils/css.py +70 -0
- flask_commands/utils/databases.py +32 -0
- flask_commands/utils/files.py +88 -0
- flask_commands/utils/models.py +119 -0
- flask_commands/utils/naming.py +98 -0
- flask_commands/utils/routes.py +312 -0
- flask_commands/utils/scaffold.py +54 -0
- flask_commands/utils/venv.py +48 -0
- flask_commands/utils/views.py +65 -0
- flask_commands-0.1.0.dist-info/METADATA +80 -0
- flask_commands-0.1.0.dist-info/RECORD +41 -0
- flask_commands-0.1.0.dist-info/WHEEL +4 -0
- flask_commands-0.1.0.dist-info/entry_points.txt +3 -0
- flask_commands-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from flask_commands.cli import cli
|
flask_commands/cli.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from flask_commands.commands.new import new
|
|
3
|
+
from flask_commands.commands.view import make_view
|
|
4
|
+
|
|
5
|
+
@click.group()
|
|
6
|
+
def cli() -> None:
|
|
7
|
+
"""Flask command line tools that will help you build a flask application with blueprints quickly."""
|
|
8
|
+
pass # pragma: no cover
|
|
9
|
+
|
|
10
|
+
# Add commands to the CLI
|
|
11
|
+
cli.add_command(new)
|
|
12
|
+
cli.add_command(make_view)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
import shutil
|
|
4
|
+
from flask_commands.utils.venv import create_venv
|
|
5
|
+
from flask_commands.utils.files import copy_templates
|
|
6
|
+
from flask_commands.utils.css import install_tailwind
|
|
7
|
+
from flask_commands.utils.databases import install_sqlitedb
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
@click.argument("project_name")
|
|
11
|
+
@click.option("--db/--no-db", default=None, help="Include a database with your project")
|
|
12
|
+
def new(project_name, db):
|
|
13
|
+
"""Create a new Flask project"""
|
|
14
|
+
project_path = os.path.abspath(project_name)
|
|
15
|
+
project_started = False
|
|
16
|
+
if os.path.exists(project_path):
|
|
17
|
+
click.secho(f"💣 Error: Folder Already Exists.", fg="red", bold=True)
|
|
18
|
+
click.secho(f" - Folder '{project_name}' already exists in this directory", fg="red")
|
|
19
|
+
click.secho(f" - Please choose a different project name or change to a new directory", fg="red")
|
|
20
|
+
return
|
|
21
|
+
try:
|
|
22
|
+
project_started = True
|
|
23
|
+
include_db = db if db is not None else click.confirm("Include a Sqlite Database?", default=True)
|
|
24
|
+
os.makedirs(project_path)
|
|
25
|
+
|
|
26
|
+
# Create a Virtual Enviroment and install dependancies and
|
|
27
|
+
# generate a requirments file
|
|
28
|
+
packages = ["Flask", "python-dotenv"]
|
|
29
|
+
if include_db:
|
|
30
|
+
packages.extend(["Flask-Login", "Flask-Migrate", "Flask-SQLAlchemy"])
|
|
31
|
+
create_venv(
|
|
32
|
+
project_path,
|
|
33
|
+
packages=packages,
|
|
34
|
+
freeze_requirements=True)
|
|
35
|
+
|
|
36
|
+
copy_templates(
|
|
37
|
+
project_path,
|
|
38
|
+
include_db=include_db,
|
|
39
|
+
replacements={"project_name": project_name, "project_path": project_path})
|
|
40
|
+
|
|
41
|
+
# Make run.sh executable
|
|
42
|
+
os.chmod(os.path.join(project_path, "run.sh"), 0o755)
|
|
43
|
+
|
|
44
|
+
install_tailwind(project_path)
|
|
45
|
+
|
|
46
|
+
if include_db:
|
|
47
|
+
install_sqlitedb(project_path)
|
|
48
|
+
|
|
49
|
+
click.secho(
|
|
50
|
+
f"{project_name.title()} is ready!!! Run the following:",
|
|
51
|
+
bold=True, underline=True)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
click.secho(f"cd {project_name}", fg="cyan")
|
|
55
|
+
click.secho("./run.sh", fg="cyan")
|
|
56
|
+
except Exception as exception:
|
|
57
|
+
if project_started and os.path.exists(project_name):
|
|
58
|
+
shutil.rmtree(project_name, ignore_errors=True)
|
|
59
|
+
click.secho("💣 Error: Project Creation Failed 😤", bold=True, fg="red")
|
|
60
|
+
raise click.ClickException(f"exception:\n{exception}") from exception
|
|
File without changes
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
|
|
4
|
+
from flask_commands.utils.controllers import (
|
|
5
|
+
controller_add_method,
|
|
6
|
+
controller_infer_name_from,
|
|
7
|
+
controller_make_file
|
|
8
|
+
)
|
|
9
|
+
from flask_commands.utils.models import (
|
|
10
|
+
model_infer_name_from,
|
|
11
|
+
model_make_file
|
|
12
|
+
)
|
|
13
|
+
from flask_commands.utils.naming import camel_to_snake
|
|
14
|
+
from flask_commands.utils.routes import (
|
|
15
|
+
route_add_method,
|
|
16
|
+
route_infer_name_from,
|
|
17
|
+
route_make_directory_and_register_blueprint,
|
|
18
|
+
generate_route_folder_path_and_blueprint_name
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from flask_commands.utils.scaffold import split_dotted_path
|
|
22
|
+
|
|
23
|
+
from flask_commands.utils.views import view_make_file
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.command(name="make:view")
|
|
27
|
+
@click.argument("dotted_path_with_name")
|
|
28
|
+
@click.option("--controller", "controller_name", default=None,
|
|
29
|
+
help="Optional controller class name (example PostController).")
|
|
30
|
+
@click.option("-c", "--generate-controller", is_flag=True,
|
|
31
|
+
help="Optional controller flag to generate an inferred controller from the dotted path name.")
|
|
32
|
+
@click.option("--route", "route_name", default=None,
|
|
33
|
+
help="Optional route class name (example /posts).")
|
|
34
|
+
@click.option("-r", "--generate-route", is_flag=True,
|
|
35
|
+
help="Optional route flag to generate an inferred route from the dotted path name.")
|
|
36
|
+
@click.option("--model", "model_name", default=None,
|
|
37
|
+
help="Optional model name (example Post which makes the database table 'posts').")
|
|
38
|
+
@click.option("-m", "--generate-model", is_flag=True,
|
|
39
|
+
help="Optional model flag to generate an inferred model from the dotted path name.")
|
|
40
|
+
def make_view(
|
|
41
|
+
dotted_path_with_name: str,
|
|
42
|
+
controller_name: str | None,
|
|
43
|
+
generate_controller: bool,
|
|
44
|
+
route_name: str | None,
|
|
45
|
+
generate_route: bool,
|
|
46
|
+
model_name: str | None,
|
|
47
|
+
generate_model: bool) -> None:
|
|
48
|
+
"""
|
|
49
|
+
\b
|
|
50
|
+
Create a template view file under app/templates/<folder>/<name>.html.
|
|
51
|
+
You can also optionally connect this view to a controller, route, and model.
|
|
52
|
+
\b
|
|
53
|
+
─── Understanding DOTTED_PATH_WITH_NAME ───
|
|
54
|
+
The dotted path defines the folder and file name:
|
|
55
|
+
<folder>.<name> → app/templates/<folder>/<name>.html
|
|
56
|
+
Example: posts.index → app/templates/posts/index.html
|
|
57
|
+
\b
|
|
58
|
+
You can also nest folders for relationships:
|
|
59
|
+
admin.users.index → app/templates/admin/users/index.html
|
|
60
|
+
posts.images.index → app/templates/posts/images/index.html
|
|
61
|
+
\b
|
|
62
|
+
─── Simple Component Views ───
|
|
63
|
+
For standalone components like a button:
|
|
64
|
+
flask make:view button
|
|
65
|
+
\b
|
|
66
|
+
─── CRUD Views ───
|
|
67
|
+
For RESTful actions (index, show, create, store, edit, update, destroy/delete):
|
|
68
|
+
Initial CRUD setup (controller, route, and model):
|
|
69
|
+
flask make:view posts.index -crm
|
|
70
|
+
flask make:view posts.index --controller PostController --route /posts --model Post
|
|
71
|
+
\b
|
|
72
|
+
Additional CRUD actions (e.g., show):
|
|
73
|
+
flask make:view posts.show -cr
|
|
74
|
+
flask make:view posts.show --controller PostController --route /posts/<int:post_id>
|
|
75
|
+
\b
|
|
76
|
+
─── Flags ───
|
|
77
|
+
Optional flags can be combined as seen above:
|
|
78
|
+
-c / --generate-controller generate inferred controller
|
|
79
|
+
-r / --generate-route generate inferred route
|
|
80
|
+
-m / --generate-model generate inferred model
|
|
81
|
+
\b
|
|
82
|
+
If you prefer explicit control:
|
|
83
|
+
--controller CONTROLLER_NAME set a specific controller
|
|
84
|
+
--route ROUTE_NAME set a specific route
|
|
85
|
+
--model MODEL_NAME set a specific model
|
|
86
|
+
"""
|
|
87
|
+
relative_path, action = split_dotted_path(dotted_path_with_name)
|
|
88
|
+
|
|
89
|
+
# Infer controller name if not provided
|
|
90
|
+
if generate_controller and controller_name is None:
|
|
91
|
+
if relative_path != '':
|
|
92
|
+
controller_name = controller_infer_name_from(relative_path)
|
|
93
|
+
click.secho(f"💡 Info: Inferred controller name as {click.style(controller_name, bold=True)}", fg="cyan")
|
|
94
|
+
else:
|
|
95
|
+
click.secho(f"⚠️ Warning: Could not infer the controller name "
|
|
96
|
+
f"from {dotted_path_with_name}", fg="yellow", bold=True)
|
|
97
|
+
|
|
98
|
+
# Infer route name if not provided
|
|
99
|
+
if generate_route and route_name is None:
|
|
100
|
+
route_name = route_infer_name_from(dotted_path_with_name)
|
|
101
|
+
click.secho("💡 Info: Inferred route name as "
|
|
102
|
+
f"{click.style(route_name, bold=True)}", fg="cyan")
|
|
103
|
+
|
|
104
|
+
# Infer model name if not provided
|
|
105
|
+
if generate_model and model_name is None:
|
|
106
|
+
model_name = model_infer_name_from(relative_path, dotted_path_with_name)
|
|
107
|
+
click.secho(f"💡 Info: Inferred model name as "
|
|
108
|
+
f"{click.style(model_name, bold=True)}", fg="cyan")
|
|
109
|
+
|
|
110
|
+
click.echo("\n")
|
|
111
|
+
|
|
112
|
+
relative_view_file_path = os.path.join(relative_path, f"{action}.html")
|
|
113
|
+
destination_file_path = \
|
|
114
|
+
os.path.join("app", "templates", relative_view_file_path)
|
|
115
|
+
|
|
116
|
+
is_successful, message = view_make_file(destination_file_path)
|
|
117
|
+
click.echo(message)
|
|
118
|
+
|
|
119
|
+
# If a controller_name was provided or inferred
|
|
120
|
+
if controller_name:
|
|
121
|
+
controller_file_path = \
|
|
122
|
+
os.path.join(
|
|
123
|
+
"app",
|
|
124
|
+
"controllers",
|
|
125
|
+
f"{camel_to_snake(controller_name)}.py")
|
|
126
|
+
|
|
127
|
+
# if controller exist just add the method
|
|
128
|
+
if os.path.exists(controller_file_path):
|
|
129
|
+
is_successful, message = controller_add_method(
|
|
130
|
+
controller_name, action, relative_view_file_path, route_name)
|
|
131
|
+
# else create the controller and the method
|
|
132
|
+
else:
|
|
133
|
+
is_successful, message = controller_make_file(
|
|
134
|
+
controller_name, action, relative_view_file_path, route_name)
|
|
135
|
+
click.echo(message)
|
|
136
|
+
|
|
137
|
+
# If a controller_name was provided or inferred
|
|
138
|
+
if route_name:
|
|
139
|
+
route_folder_path, blueprint_name = \
|
|
140
|
+
generate_route_folder_path_and_blueprint_name(
|
|
141
|
+
dotted_path_with_name, relative_path)
|
|
142
|
+
try:
|
|
143
|
+
if os.path.exists(route_folder_path):
|
|
144
|
+
is_successful, message = \
|
|
145
|
+
route_add_method(
|
|
146
|
+
relative_path, # this is everything before the last part of dotted_path_with_name replacing . with /
|
|
147
|
+
action, # in CRUD this is index, create, update, show... else this is just the last part of dotted_path_with_name
|
|
148
|
+
route_folder_path, # this is app/routes/{relative_path} or app/routes/main if relative path is ''
|
|
149
|
+
blueprint_name, # posts or mains or posts_comments
|
|
150
|
+
route_name, # this is the url path like /posts/<int:post_id> or /admin/posts/comments
|
|
151
|
+
controller_name) # contoller_name is like post_controller
|
|
152
|
+
else:
|
|
153
|
+
is_successful, message = \
|
|
154
|
+
route_make_directory_and_register_blueprint(
|
|
155
|
+
# relative_path, # this is everything before the last part of dotted_path_with_name replacing . with /
|
|
156
|
+
action, # in CRUD this is index, create, update, show... else this is just the last part of dotted_path_with_name
|
|
157
|
+
route_folder_path, # this is app/routes/{relative_path} or app/routes/main if relative path is ''
|
|
158
|
+
blueprint_name, # posts or mains or posts_comments
|
|
159
|
+
route_name, # this is the url path like /posts/<int:post_id> or /admin/posts/comments
|
|
160
|
+
controller_name) # contoller_name is like post_controller
|
|
161
|
+
click.echo(message)
|
|
162
|
+
except Exception as exception:
|
|
163
|
+
click.secho(f"💣 Error:\n {exception}", fg="red")
|
|
164
|
+
|
|
165
|
+
# If a model_name was provided or inferred
|
|
166
|
+
if model_name:
|
|
167
|
+
model_init_path = os.path.join("app", "models", "__init__.py")
|
|
168
|
+
model_file_path = os.path.join("app", "models", f"{model_name.lower()}.py")
|
|
169
|
+
is_successful, message = model_make_file(
|
|
170
|
+
model_name, model_init_path, model_file_path)
|
|
171
|
+
click.echo(message)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
SECRET_KEY=PUT_SOMETHING_SECRET_HERE
|
|
2
|
+
FLASK_APP=run.py
|
|
3
|
+
FLASK_CONFIG=development
|
|
4
|
+
APP_NAME=project_name
|
|
5
|
+
SQLALCHEMY_DEVELOPMENT_DATABASE_URI=sqlite:///project_name_dev.db
|
|
6
|
+
SQLALCHEMY_PRODUCTION_DATABASE_URI=mysql+pymysql://username:password@localhost:3306/project_name_prod
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from config import config
|
|
2
|
+
from flask import Flask
|
|
3
|
+
from flask_login import LoginManager
|
|
4
|
+
from flask_migrate import Migrate
|
|
5
|
+
from flask_sqlalchemy import SQLAlchemy
|
|
6
|
+
|
|
7
|
+
# Logging a user's authentication state handler
|
|
8
|
+
login_manager = LoginManager()
|
|
9
|
+
|
|
10
|
+
# ORM Database handler
|
|
11
|
+
db = SQLAlchemy()
|
|
12
|
+
migrate = Migrate()
|
|
13
|
+
|
|
14
|
+
def create_app(config_name) -> Flask:
|
|
15
|
+
"""Creates a Flask application Instance."""
|
|
16
|
+
app = Flask(__name__)
|
|
17
|
+
|
|
18
|
+
# apply configuration
|
|
19
|
+
app.config.from_object(config[config_name])
|
|
20
|
+
|
|
21
|
+
# initialize extensions: order matters
|
|
22
|
+
login_manager.init_app(app)
|
|
23
|
+
db.init_app(app)
|
|
24
|
+
migrate.init_app(app, db)
|
|
25
|
+
|
|
26
|
+
from app import models
|
|
27
|
+
|
|
28
|
+
from app.routes.mains import bp as mains_blueprint
|
|
29
|
+
app.register_blueprint(mains_blueprint)
|
|
30
|
+
|
|
31
|
+
return app
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main_controller import MainController
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .user import User
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from app import db, login_manager
|
|
2
|
+
from flask_login import UserMixin
|
|
3
|
+
from werkzeug.security import generate_password_hash, check_password_hash
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
|
|
6
|
+
class User(UserMixin, db.Model):
|
|
7
|
+
__tablename__ = 'users'
|
|
8
|
+
# Columns
|
|
9
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
10
|
+
username = db.Column(db.String(64), index=True,
|
|
11
|
+
unique=True, nullable=False)
|
|
12
|
+
password_hash = db.Column(db.String(256), nullable=True)
|
|
13
|
+
created_at = db.Column(db.DateTime(timezone=True),
|
|
14
|
+
index=True,
|
|
15
|
+
default=lambda: datetime.now(timezone.utc))
|
|
16
|
+
updated_at = db.Column(db.DateTime(timezone=True),
|
|
17
|
+
default=lambda: datetime.now(timezone.utc),
|
|
18
|
+
onupdate=lambda: datetime.now(timezone.utc))
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def password(self):
|
|
22
|
+
"""Throw an error when trying to access password attribute"""
|
|
23
|
+
raise AttributeError('password is not a readable attribute')
|
|
24
|
+
|
|
25
|
+
@password.setter
|
|
26
|
+
def password(self, password):
|
|
27
|
+
"""Hash and set password"""
|
|
28
|
+
self.password_hash = generate_password_hash(password)
|
|
29
|
+
|
|
30
|
+
def verify_password(self, password):
|
|
31
|
+
"""Returns true or false by verifing a given password"""
|
|
32
|
+
return check_password_hash(self.password_hash, password)
|
|
33
|
+
|
|
34
|
+
def store_in_database(self):
|
|
35
|
+
db.session.add(self)
|
|
36
|
+
db.session.commit()
|
|
37
|
+
|
|
38
|
+
def delete_from_database(self):
|
|
39
|
+
db.session.delete(self)
|
|
40
|
+
db.session.commit()
|
|
41
|
+
|
|
42
|
+
def __repr__(self):
|
|
43
|
+
"""Model representation for code debugging"""
|
|
44
|
+
return f'<User id:{self.id} username:{self.username}>'
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@login_manager.user_loader
|
|
48
|
+
def load_user(user_id):
|
|
49
|
+
"""Load the user from the database, given the id stored
|
|
50
|
+
in the session"""
|
|
51
|
+
return User.query.get(int(user_id))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Document</title>
|
|
7
|
+
{%- block styles %}
|
|
8
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='tailwind.min.css', v=time.time()) }}">
|
|
9
|
+
{%- endblock styles %}
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
Hello World
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
# Import Environment variables
|
|
6
|
+
|
|
7
|
+
load_dotenv() # reads variables from a .env file and sets them in os.environ
|
|
8
|
+
|
|
9
|
+
class BaseConfig():
|
|
10
|
+
FLASK_CONFIG = os.environ.get('FLASK_CONFIG')
|
|
11
|
+
SECRET_KEY = os.environ.get('SECRET_KEY')
|
|
12
|
+
APP_NAME = os.environ.get('APP_NAME')
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from logging.handlers import SMTPHandler, RotatingFileHandler
|
|
5
|
+
from app import create_app
|
|
6
|
+
|
|
7
|
+
app = create_app(os.getenv('FLASK_CONFIG') or 'development')
|
|
8
|
+
|
|
9
|
+
# These are global variable for your jinja2 templates
|
|
10
|
+
@app.context_processor
|
|
11
|
+
def inject_globals():
|
|
12
|
+
return {
|
|
13
|
+
'time': time}
|
|
14
|
+
|
|
15
|
+
with app.app_context():
|
|
16
|
+
if not os.path.exists('logs'):
|
|
17
|
+
os.makedirs('logs', exist_ok=True)
|
|
18
|
+
|
|
19
|
+
max_bytes = 10 * 1024 * 1024 # 10 MB
|
|
20
|
+
backup_count = 10 # this will create 10 files .log.1 .log.2 .log.3 ...
|
|
21
|
+
|
|
22
|
+
# Logging levels to: NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
23
|
+
|
|
24
|
+
file_handler = RotatingFileHandler(
|
|
25
|
+
f"logs/{app.config['APP_NAME']}-{app.config['FLASK_CONFIG']}.log",
|
|
26
|
+
maxBytes=max_bytes,
|
|
27
|
+
backupCount=backup_count)
|
|
28
|
+
file_handler.setFormatter(logging.Formatter(
|
|
29
|
+
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
|
30
|
+
if file_handler not in app.logger.handlers:
|
|
31
|
+
app.logger.addHandler(file_handler)
|
|
32
|
+
|
|
33
|
+
# SQL log file (separate from app logs)
|
|
34
|
+
sql_file_handler = RotatingFileHandler(
|
|
35
|
+
f"logs/sql-{app.config['FLASK_CONFIG']}.log",
|
|
36
|
+
maxBytes=max_bytes,
|
|
37
|
+
backupCount=backup_count)
|
|
38
|
+
sql_file_handler.setFormatter(logging.Formatter(
|
|
39
|
+
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
|
|
40
|
+
sqlalchemy_logger = logging.getLogger('sqlalchemy.engine')
|
|
41
|
+
sqlalchemy_logger.propagate = False
|
|
42
|
+
if sql_file_handler not in sqlalchemy_logger.handlers:
|
|
43
|
+
sqlalchemy_logger.addHandler(sql_file_handler)
|
|
44
|
+
|
|
45
|
+
if app.config['FLASK_CONFIG'] == 'development':
|
|
46
|
+
# this sets what the application will emit defaults to WARNING
|
|
47
|
+
app.logger.setLevel(logging.DEBUG)
|
|
48
|
+
sqlalchemy_logger.setLevel(logging.INFO)
|
|
49
|
+
|
|
50
|
+
# this sets what is writting to the logs
|
|
51
|
+
file_handler.setLevel(logging.DEBUG)
|
|
52
|
+
sql_file_handler.setLevel(logging.INFO)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if app.config['FLASK_CONFIG'] == 'production':
|
|
56
|
+
app.logger.setLevel(logging.INFO)
|
|
57
|
+
sqlalchemy_logger.setLevel(logging.WARNING)
|
|
58
|
+
|
|
59
|
+
file_handler.setLevel(logging.INFO)
|
|
60
|
+
sql_file_handler.setLevel(logging.WARNING)
|
|
61
|
+
|
|
62
|
+
app.logger.info(f"{app.config['APP_NAME']} started up in {app.config['FLASK_CONFIG']} mode")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# The main Terminal will eventually run the Hot Reloading
|
|
2
|
+
|
|
3
|
+
# Start Up Shell
|
|
4
|
+
osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
|
|
5
|
+
osascript -e 'tell application "Terminal" to do script "cd project_path && source venv/bin/activate" in front window'
|
|
6
|
+
osascript -e 'tell application "Terminal" to do script "flask shell" in front window'
|
|
7
|
+
|
|
8
|
+
# Start Up Flask Server
|
|
9
|
+
osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
|
|
10
|
+
osascript -e 'tell application "Terminal" to do script "cd project_path && source venv/bin/activate" in front window'
|
|
11
|
+
osascript -e 'tell application "Terminal" to do script "flask run --debug" in front window'
|
|
12
|
+
|
|
13
|
+
# Building your tailwind.css file
|
|
14
|
+
osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
|
|
15
|
+
osascript -e 'tell application "Terminal" to do script "cd project_path && npm run watch:css" in front window'
|
|
16
|
+
|
|
17
|
+
# Building your tailwind.min.css file
|
|
18
|
+
osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
|
|
19
|
+
osascript -e 'tell application "Terminal" to do script "cd project_path && npm run build:css" in front window'
|
|
20
|
+
|
|
21
|
+
# Open up VS code Text
|
|
22
|
+
osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
|
|
23
|
+
osascript -e 'tell application "Terminal" to do script "cd project_path && code ." in front window'
|
|
24
|
+
|
|
25
|
+
sleep 5
|
|
26
|
+
|
|
27
|
+
# Open Safari and navigate to http://127.0.0.1:5000
|
|
28
|
+
open -a "Safari" "http://127.0.0.1:5000"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
#!/bin/bash
|
|
32
|
+
|
|
33
|
+
# Define the folder to watch
|
|
34
|
+
WATCH_FOLDER_TEMPLATES="$(pwd)/app/templates"
|
|
35
|
+
WATCH_FOLDER_CONTROLLER="$(pwd)/app/controllers"
|
|
36
|
+
WATCH_FOLDER_FORMS="$(pwd)/app/forms"
|
|
37
|
+
WATCH_FOLDER_MODELS="$(pwd)/app/models"
|
|
38
|
+
WATCH_FOLDER_ROUTES="$(pwd)/app/routes"
|
|
39
|
+
|
|
40
|
+
# Function to refresh Chrome
|
|
41
|
+
refresh_safari() {
|
|
42
|
+
osascript <<EOF
|
|
43
|
+
tell application "Safari"
|
|
44
|
+
repeat with w in windows
|
|
45
|
+
repeat with t in tabs of w
|
|
46
|
+
if (URL of t contains "http://127.0.0.1:5000/") then
|
|
47
|
+
tell t to do JavaScript "window.location.reload();"
|
|
48
|
+
end if
|
|
49
|
+
end repeat
|
|
50
|
+
end repeat
|
|
51
|
+
end tell
|
|
52
|
+
EOF
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Watch for file changes in the folder and its subfolders
|
|
57
|
+
fswatch -0 "$WATCH_FOLDER_TEMPLATES" "$WATCH_FOLDER_CONTROLLER" "$WATCH_FOLDER_FORMS" "$WATCH_FOLDER_MODELS" "$WATCH_FOLDER_ROUTES" | while read -d "" event
|
|
58
|
+
|
|
59
|
+
do
|
|
60
|
+
echo "Change detected: $event"
|
|
61
|
+
|
|
62
|
+
# Call the function to refresh Chrome
|
|
63
|
+
refresh_safari
|
|
64
|
+
done
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# intentionally empty
|