hm_ai_qc_report_tool/app.py
nickviljoen 9c33858726 Add campaign presentation management and global pricing reference
Introduces a new Campaigns module for uploading campaign presentation PDFs
that QC checks reference to validate assets against campaign-specific
guidelines (typography, layout, copy, pricing format). Also adds a global
pricing reference system that maps country codes to currency symbols and
formats for deterministic price/currency validation.

- New CampaignPresentation model + campaigns blueprint with CRUD routes
- PDF parsing via LlamaParse (text + multimodal page images)
- Global pricing PDF parsed into structured JSON lookup
- Campaign context injected into both image and video QC executors
- Quality checks enhanced with campaign guidelines in LLM prompts
- Price/currency check uses global pricing lookup (saves an LLM call)
- Campaign dropdown added to HM QC and Video QC configure pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:12:22 +02:00

170 lines
5.1 KiB
Python

"""
Flask Application Factory for Unified HM QC Platform.
This application merges multiple QC tools into a single platform with:
- HM QC (PDF/image quality control)
- Video QC (video quality control)
- Video Master Adot Detection (video matching)
- Reporting (consolidated reports from Box.com and QC modules)
"""
import logging
import os
from flask import Flask, render_template, session, request, redirect, url_for
# Import configuration
import config as app_config
# Import core modules
from core.auth.middleware import AuthMiddleware
from core.models.database import init_db, db
from core.services.box_client import BoxReportClient
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def create_app(config_class=app_config.Config):
"""
Application factory function.
Args:
config_class: Configuration class to use
Returns:
Configured Flask application instance
"""
# Initialize Flask app
app = Flask(__name__)
app.config.from_object(config_class)
# Create necessary directories
os.makedirs('database', exist_ok=True)
os.makedirs('uploads/hm_qc', exist_ok=True)
os.makedirs('uploads/video_qc', exist_ok=True)
os.makedirs('uploads/video_master', exist_ok=True)
os.makedirs('storage/reports/hm_qc', exist_ok=True)
os.makedirs('storage/reports/consolidated', exist_ok=True)
os.makedirs('storage/campaigns', exist_ok=True)
os.makedirs('storage/reference', exist_ok=True)
# Initialize database
init_db(app)
logger.info("Database initialized")
# Initialize authentication middleware (simple session-based auth)
auth = AuthMiddleware(app)
logger.info("Authentication initialized")
app.auth = auth
# Initialize Box client (lazy loading)
app._box_client = None
def get_box_client():
"""Get or initialize Box client."""
if app._box_client is None:
try:
app._box_client = BoxReportClient(
config_path=app.config['BOX_CONFIG_PATH'],
report_folder_id=app.config['BOX_REPORT_FOLDER_ID']
)
logger.info("Box client initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Box client: {e}")
raise
return app._box_client
# Store box client getter in app context
app.get_box_client = get_box_client
# Register blueprints
# Auth blueprint (for login/logout endpoints)
from core.auth.routes import auth_bp
app.register_blueprint(auth_bp)
logger.info("Auth blueprint registered at /auth")
# Require login for all routes except auth endpoints and static files
@app.before_request
def require_login():
allowed_prefixes = ('/auth/', '/static/')
if any(request.path.startswith(p) for p in allowed_prefixes):
return None
if not session.get('authenticated'):
return redirect(url_for('auth.login_page'))
# Task #3: Reporting blueprint (COMPLETED)
from modules.reporting import reporting_bp
app.register_blueprint(reporting_bp)
logger.info("Reporting blueprint registered at /reporting")
# Task #4: HM QC blueprint (COMPLETED)
from modules.hm_qc import hm_qc_bp
app.register_blueprint(hm_qc_bp)
logger.info("HM QC blueprint registered at /hm-qc")
# Task #5: Video QC blueprint (BETA)
from modules.video_qc import video_qc_bp
app.register_blueprint(video_qc_bp)
logger.info("Video QC blueprint (BETA) registered at /video-qc")
# Task #6: Video Master blueprint (BETA)
from modules.video_master import video_master_bp
app.register_blueprint(video_master_bp)
logger.info("Video Master blueprint (BETA) registered at /video-master")
# Usage Dashboard
from modules.usage import usage_bp
app.register_blueprint(usage_bp)
logger.info("Usage dashboard registered at /usage")
# Campaign Management
from modules.campaigns import campaigns_bp
app.register_blueprint(campaigns_bp)
logger.info("Campaigns blueprint registered at /campaigns")
@app.route('/')
def root():
"""Render reporting index at root."""
return render_template('reporting/index.html', active_tab='reporting')
# Register error handlers
register_error_handlers(app)
logger.info("Application initialized successfully")
return app
def register_error_handlers(app):
"""
Register error handlers.
Args:
app: Flask application
"""
@app.errorhandler(404)
def not_found(error):
"""Handle 404 errors."""
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
"""Handle 500 errors."""
logger.error(f"Internal server error: {error}")
return render_template('500.html'), 500
# Create application instance
app = create_app()
if __name__ == '__main__':
# Run app
app.run(
host=app.config['HOST'],
port=app.config['PORT'],
debug=True
)