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>
170 lines
5.1 KiB
Python
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
|
|
)
|