Files
quality_app-v2/app/__init__.py
Quality App Developer b15cc93b9d FG Scan form validation improvements with warehouse module updates
- Fixed 3 JavaScript syntax errors in fg_scan.html (lines 951, 840-950, 1175-1215)
- Restored form field validation with proper null safety checks
- Re-enabled auto-advance between form fields
- Re-enabled CP code auto-complete with hyphen detection
- Updated validation error messages with clear format specifications and examples
- Added autocomplete='off' to all input fields
- Removed auto-prefix correction feature
- Updated warehouse routes and modules for box assignment workflow
- Added/improved database initialization scripts
- Updated requirements.txt dependencies

Format specifications implemented:
- Operator Code: OP + 2 digits (example: OP01, OP99)
- CP Code: CP + 8 digits + hyphen + 4 digits (example: CP00000000-0001)
- OC1/OC2 Codes: OC + 2 digits (example: OC01, OC99)
- Defect Code: 3 digits only
2026-01-30 10:50:06 +02:00

201 lines
6.1 KiB
Python

"""
Quality App v2 - Flask Application Factory
Robust, modular application with login, dashboard, and multiple modules
"""
from flask import Flask
from flask_session import Session
from datetime import datetime, timedelta
import os
import logging
from logging.handlers import RotatingFileHandler
def create_app(config=None):
"""
Application factory function
Creates and configures the Flask application
"""
app = Flask(__name__)
# Load configuration
if config is None:
from app.config import Config
config = Config
app.config.from_object(config)
# Setup logging
setup_logging(app)
logger = logging.getLogger(__name__)
logger.info("=" * 80)
logger.info("Flask App Initialization Started")
logger.info("=" * 80)
# Configure session
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=8)
app.config['SESSION_COOKIE_SECURE'] = False # Set True in production with HTTPS
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
app.config['SESSION_COOKIE_NAME'] = 'quality_app_session'
app.config['SESSION_REFRESH_EACH_REQUEST'] = True
# Use filesystem for session storage (works with multiple gunicorn workers)
sessions_dir = os.path.join(app.config.get('LOG_DIR', '/app/data/logs'), '..', 'sessions')
os.makedirs(sessions_dir, exist_ok=True)
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = sessions_dir
app.config['SESSION_FILE_THRESHOLD'] = 500
# Initialize Flask-Session
Session(app)
# Initialize database connection
logger.info("Initializing database connection...")
from app.database import init_db, close_db
init_db(app)
app.teardown_appcontext(close_db)
# Register blueprints
logger.info("Registering blueprints...")
register_blueprints(app)
# Register error handlers
logger.info("Registering error handlers...")
register_error_handlers(app)
# Add template globals
app.jinja_env.globals['now'] = datetime.now
# Add context processor for app name
@app.context_processor
def inject_app_settings():
"""Inject app settings into all templates"""
try:
from app.database import get_db
conn = get_db()
cursor = conn.cursor()
cursor.execute(
"SELECT setting_value FROM application_settings WHERE setting_key = %s",
('app_name',)
)
result = cursor.fetchone()
cursor.close()
app_name = result[0] if result else 'Quality App v2'
except:
app_name = 'Quality App v2'
return {'app_name': app_name}
# Add before_request handlers
register_request_handlers(app)
# Initialize backup scheduler
logger.info("Initializing backup scheduler...")
try:
from app.scheduler import init_scheduler
init_scheduler(app)
except Exception as e:
logger.error(f"Failed to initialize backup scheduler: {e}")
logger.info("=" * 80)
logger.info("Flask App Initialization Completed Successfully")
logger.info("=" * 80)
return app
def setup_logging(app):
"""Configure application logging"""
log_dir = app.config.get('LOG_DIR', '/app/data/logs')
# Create log directory if it doesn't exist
if not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
# Configure rotating file handler
log_file = os.path.join(log_dir, 'app.log')
handler = RotatingFileHandler(
log_file,
maxBytes=10485760, # 10MB
backupCount=10
)
# Create formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
# Set logging level
log_level = app.config.get('LOG_LEVEL', 'INFO')
handler.setLevel(getattr(logging, log_level))
# Add handler to app logger
app.logger.addHandler(handler)
app.logger.setLevel(getattr(logging, log_level))
def register_blueprints(app):
"""Register application blueprints"""
from app.routes import main_bp
from app.modules.quality.routes import quality_bp
from app.modules.settings.routes import settings_bp
from app.modules.warehouse.routes import warehouse_bp
from app.modules.warehouse.boxes_routes import boxes_bp
app.register_blueprint(main_bp)
app.register_blueprint(quality_bp, url_prefix='/quality')
app.register_blueprint(settings_bp, url_prefix='/settings')
app.register_blueprint(warehouse_bp, url_prefix='/warehouse')
app.register_blueprint(boxes_bp)
app.logger.info("Blueprints registered: main, quality, settings, warehouse, boxes")
def register_error_handlers(app):
"""Register error handlers"""
@app.errorhandler(404)
def page_not_found(e):
from flask import render_template
return render_template('errors/404.html'), 404
@app.errorhandler(500)
def internal_error(e):
from flask import render_template
app.logger.error(f"Internal error: {e}")
return render_template('errors/500.html'), 500
@app.errorhandler(403)
def forbidden(e):
from flask import render_template
return render_template('errors/403.html'), 403
def register_request_handlers(app):
"""Register before/after request handlers"""
@app.before_request
def before_request():
"""Handle pre-request logic"""
from flask import session, request, redirect, url_for
# Skip authentication check for login and static files
if request.endpoint and (
request.endpoint in ['static', 'main.login', 'main.index'] or
request.path.startswith('/static/')
):
return None
# Check if user is logged in
if 'user_id' not in session:
return redirect(url_for('main.login'))
return None
@app.after_request
def after_request(response):
"""Handle post-request logic"""
return response