158 lines
5.1 KiB
Python
158 lines
5.1 KiB
Python
"""
|
|
Employee Workstation Activity Tracking - Flask Application Factory
|
|
|
|
This module provides the application factory function 'create_app' that initializes
|
|
the Flask application with its configuration, database connection, and registered blueprints.
|
|
"""
|
|
import logging
|
|
import os
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
from dotenv import load_dotenv
|
|
from flask import Flask
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
from sqlalchemy import event
|
|
|
|
# Initialize SQLAlchemy globally to avoid circular imports
|
|
db = SQLAlchemy()
|
|
|
|
|
|
def create_app(test_config=None):
|
|
"""Create and configure the Flask application using the factory pattern."""
|
|
|
|
# Load environment variables
|
|
load_dotenv() # Try .env first
|
|
config_env_path = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)), "config.env"
|
|
)
|
|
# Defer logging for config.env until logger is configured
|
|
config_env_loaded_message = None
|
|
config_env_warning_message = None
|
|
if os.path.exists(config_env_path):
|
|
load_dotenv(config_env_path)
|
|
config_env_loaded_message = (
|
|
f"Loaded environment variables from {config_env_path}"
|
|
)
|
|
else:
|
|
config_env_warning_message = (
|
|
f"Warning: config.env file not found at {config_env_path}"
|
|
)
|
|
|
|
# Get the project root directory (parent of app directory)
|
|
project_root = os.path.dirname(os.path.dirname(__file__))
|
|
|
|
# Create and configure the app with template folder in project root
|
|
app = Flask(
|
|
__name__,
|
|
instance_relative_config=True,
|
|
template_folder=os.path.join(project_root, "templates"),
|
|
static_folder=os.path.join(project_root, "static"),
|
|
)
|
|
|
|
# Default configuration
|
|
app.config.from_mapping(
|
|
SECRET_KEY=os.environ.get("SECRET_KEY", "dev"),
|
|
SQLALCHEMY_DATABASE_URI=os.environ.get(
|
|
"DATABASE_URI", os.environ.get("DATABASE_URL")
|
|
),
|
|
SQLALCHEMY_TRACK_MODIFICATIONS=False,
|
|
)
|
|
|
|
# Override configuration with test config if provided
|
|
if test_config is not None:
|
|
app.config.update(test_config)
|
|
|
|
# Ensure DATABASE_URI is set
|
|
if not app.config["SQLALCHEMY_DATABASE_URI"]:
|
|
raise ValueError(
|
|
"DATABASE_URI or DATABASE_URL environment variable must be set"
|
|
)
|
|
|
|
# Ensure the instance folder exists
|
|
try:
|
|
os.makedirs(app.instance_path)
|
|
except OSError:
|
|
pass
|
|
|
|
# Configure logging
|
|
if not app.debug: # Avoid duplicate logs in debug mode
|
|
log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
|
log_handler = RotatingFileHandler(
|
|
os.path.join(app.instance_path, "server.log"),
|
|
maxBytes=1024 * 1024 * 5, # 5 MB
|
|
backupCount=5,
|
|
)
|
|
log_handler.setFormatter(log_formatter)
|
|
log_handler.setLevel(logging.INFO)
|
|
app.logger.addHandler(log_handler)
|
|
app.logger.setLevel(logging.INFO)
|
|
|
|
# Log config.env messages now that logger is available
|
|
if config_env_loaded_message:
|
|
app.logger.info(config_env_loaded_message)
|
|
if config_env_warning_message:
|
|
app.logger.warning(config_env_warning_message)
|
|
|
|
app.logger.info("Flask application starting up...")
|
|
|
|
# Initialize the database with the app
|
|
db.init_app(app)
|
|
|
|
# Set up event listener for PostgreSQL timezone
|
|
with app.app_context():
|
|
if "postgresql" in app.config["SQLALCHEMY_DATABASE_URI"]:
|
|
|
|
@event.listens_for(db.engine, "connect")
|
|
def set_timezone(dbapi_connection, connection_record):
|
|
cursor = dbapi_connection.cursor()
|
|
cursor.execute("SET timezone TO 'Asia/Dubai';")
|
|
cursor.close()
|
|
app.logger.info("Set PostgreSQL session timezone to 'Asia/Dubai' (UTC+4)")
|
|
app.logger.info("Note: Application uses UTC internally, database is set to Asia/Dubai time.")
|
|
|
|
# Import and register blueprints
|
|
from app.api import events_bp, reports_bp
|
|
|
|
app.register_blueprint(events_bp)
|
|
app.register_blueprint(reports_bp)
|
|
|
|
from app.views.dashboard import views_bp
|
|
|
|
app.register_blueprint(views_bp)
|
|
|
|
# Register error handlers
|
|
from app.errors import register_error_handlers
|
|
|
|
register_error_handlers(app)
|
|
|
|
# Initialize database tables when in development
|
|
@app.cli.command("init-db")
|
|
def init_db_command():
|
|
"""Clear existing data and create new tables."""
|
|
with app.app_context():
|
|
db.create_all()
|
|
app.logger.info("Database tables created")
|
|
|
|
@app.route("/healthcheck")
|
|
def healthcheck():
|
|
return {"status": "ok"}, 200
|
|
|
|
# Initialize and start the scheduler
|
|
from app.scheduler import init_scheduler
|
|
init_scheduler(app)
|
|
|
|
# Register custom CLI commands
|
|
from app.cli import register_cli_commands
|
|
register_cli_commands(app)
|
|
|
|
return app
|
|
|
|
|
|
def init_db(app_instance):
|
|
"""Initialize the database outside of the CLI context using a provided app
|
|
instance."""
|
|
# app = create_app() # No longer creating app here, using provided instance
|
|
with app_instance.app_context():
|
|
db.create_all()
|
|
app_instance.logger.info("Database initialized via init_db function")
|