No description
Find a file
nickviljoen c8aaf3833b Merge develop into main for v3.1.0 release
Video QC tuning (Plan 1):
  - price_currency: surface matched price + product in card body
  - garment_name (new): Gemini text-overlay detection + deterministic
    match vs PricingReference.product_name for the file's locale
  - title_safe (new, advisory weight=0): flag price/garment text
    inside platform UI overlay zones (TikTok / IG Stories / IG Reels
    / generic vertical)

SRT subtitle QC (Plan 2):
  - modules/video_qc/utils/srt_pairing.py — pure-function pairing
    helpers (canonical_locale, normalise_slug, parse_*_tokens,
    score_pair, pair_batch)
  - srt_structure (new, deterministic): srt-library parse + UTF-8 /
    chardet encoding fallback + cue index + empty-cue checks
  - srt_timing (new, deterministic): overlaps, last-cue vs video
    duration, broadcast norms (reading speed, line length, cue
    duration)
  - srt_language (new, text-only Gemini Flash): detect SRT language
    vs expected from video locale
  - BatchVideoQCExecutor pre-flight pairing; .srt accepted in
    upload form; /pairing-preview endpoint; configure-page
    pairing summary (XSS-safe DOM rendering)

Deps added: srt==3.5.3, chardet>=5.0
No DB schema changes.
2026-05-15 21:41:06 +02:00
core Video Master: revert campaigns folder + lenient name matching 2026-05-09 20:19:35 +02:00
database Add modular architecture, core framework, and web UI 2026-02-25 11:39:04 +02:00
deploy Phase 4 prep: add Prod cutover runbook 2026-05-09 20:47:05 +02:00
docs/superpowers Add SRT subtitle QC implementation plan 2026-05-15 11:49:23 +02:00
migrations Phase 0: bootstrap Alembic, add /health, prep for Dev/Prod cutover 2026-05-09 13:47:54 +02:00
modules Video QC: fix client-side SRT rejection in upload form 2026-05-15 21:26:20 +02:00
static MSAL: ensure redirectUri always ends in trailing slash 2026-05-09 17:16:02 +02:00
templates MSAL: ensure redirectUri always ends in trailing slash 2026-05-09 17:16:02 +02:00
.dockerignore v1.2.0: Add Docker deployment, simplify auth to local login, production config 2026-03-21 14:37:53 +02:00
.env.example Video Master: revert campaigns folder + lenient name matching 2026-05-09 20:19:35 +02:00
.gitignore Reporting: filesystem-back the search-result cache 2026-05-09 17:46:42 +02:00
app.py Reporting: filesystem-back the search-result cache 2026-05-09 17:46:42 +02:00
box_client.py Initial Commit 2025-12-30 16:47:56 +02:00
CHANGELOG.md v2.5.0: Update README and CHANGELOG 2026-04-28 20:20:31 +02:00
config.py Video Master: revert campaigns folder + lenient name matching 2026-05-09 20:19:35 +02:00
deploy.sh deploy.sh: handle first-deploy and --force re-deploys 2026-05-09 16:35:36 +02:00
DEPLOYMENT_CHECKLIST.md Add modular architecture, core framework, and web UI 2026-02-25 11:39:04 +02:00
docker-compose.yml v1.2.0: Add Docker deployment, simplify auth to local login, production config 2026-03-21 14:37:53 +02:00
docker-entrypoint.sh Phase 0: bootstrap Alembic, add /health, prep for Dev/Prod cutover 2026-05-09 13:47:54 +02:00
Dockerfile Phase 0: bootstrap Alembic, add /health, prep for Dev/Prod cutover 2026-05-09 13:47:54 +02:00
DOCUMENTATION_SUMMARY.txt Update documentation for unified platform consolidation 2026-02-25 13:51:21 +02:00
gunicorn_config.py Gunicorn: raise max_requests and graceful_timeout 2026-04-28 13:17:43 +02:00
INTEGRATION_TEST_REPORT.md Add modular architecture, core framework, and web UI 2026-02-25 11:39:04 +02:00
MIGRATION_GUIDE.md Update documentation for unified platform consolidation 2026-02-25 13:51:21 +02:00
README.md v2.5.0: Update README and CHANGELOG 2026-04-28 20:20:31 +02:00
report_parser.py Initial Commit 2025-12-30 16:47:56 +02:00
requirements.txt SRT QC: add srt library to requirements 2026-05-15 20:17:40 +02:00
run.sh Reporting updated. 2026-01-14 09:14:00 +02:00
run_prod.sh Initial Commit 2025-12-30 16:47:56 +02:00
setup.sh Initial Commit 2025-12-30 16:47:56 +02:00
test_integration.py Add modular architecture, core framework, and web UI 2026-02-25 11:39:04 +02:00
test_local.sh Initial Commit 2025-12-30 16:47:56 +02:00
wait_for_db.py Phase 0: bootstrap Alembic, add /health, prep for Dev/Prod cutover 2026-05-09 13:47:54 +02:00
wsgi.py v1.2.0: Add Docker deployment, simplify auth to local login, production config 2026-03-21 14:37:53 +02:00

Unified HM QC Platform

Version: 2.5.0 Status: Production (Deployed) Deployed at: https://ai-sandbox.oliver.solutions/hm-ai-qc-report

A comprehensive quality control platform for H&M marketing assets with AI-powered validation, video matching, and consolidated reporting.


Overview

The platform integrates seven tools into a single web application:

  1. Reporting - Consolidated QC reports from Box.com with search history
  2. HM QC - AI-powered image quality control (text legibility, language, quality, pricing)
  3. Video QC - AI-powered video quality control (direct video analysis via Gemini)
  4. Video Master Adot - Campaign-based master-to-adaptation video matching via Box
  5. Printer Check - CSV-to-PDF cross-referencing for print order validation
  6. Campaigns - Campaign presentation and media plan management for QC reference
  7. Usage Dashboard - API usage tracking, token counts, and cost estimates

Key Features

  • Unified tabbed interface with H&M branding
  • Local username/password authentication
  • Multi-provider AI: OpenAI GPT-4o and Google Gemini 2.5 Flash
  • Google Gemini direct video analysis (no frame extraction needed)
  • Campaign presentation upload for guideline-based QC validation
  • Pricing references library — standalone uploadable docs (PDF or multi-sheet Excel Mastersheet), per-run selectable, independent of any campaign
  • Deterministic price matching — Excel MPC Prices sheet parsed without LLM into structured per-locale lookup (format + actual prices)
  • Video Master 3-pass matching (v2.5.0) — same-duration masters tried first, then strictly longer masters, with Gemini AI Vision as a same-duration/different-resolution fallback for crops; version-aware (only the highest V<n> per file group is matched)
  • Batch processing for both HM QC and Video QC — multi-file upload, sequential processing, batch results page, collapsible batch groupings on index
  • Batch naming by job number (entered at upload time)
  • Consolidated report generation: select multiple reports and download a combined HTML
  • Asset thumbnails in report listings and embedded in HTML reports (base64)
  • Sequential batch processing with memory-safe garbage collection
  • Real-time progress tracking (SSE + polling)
  • Docker deployment with Apache reverse proxy
  • Usage tracking with estimated costs per API call

Deployment

Docker (Production)

# Clone from Bitbucket
git clone git@bitbucket.org:zlalani/hm_ai_qc_report_tool.git /opt/hm-qc-app
cd /opt/hm-qc-app

# Configure environment
cp .env.example .env
# Edit .env with production values (see .env.example)
# Generate password: python3 deploy/generate_password.py

# Build and start
docker compose build
docker compose up -d

# Create database tables
docker exec hm-qc-app python3 -c "from app import app; from core.models.database import db; app.app_context().push(); db.create_all(); print('Tables created')"

The app runs on 127.0.0.1:5050 inside Docker. Configure Apache or Nginx as reverse proxy — see deploy/ for config snippets.

Common Commands

docker compose logs -f              # Tail logs
docker compose restart              # Quick restart
docker compose down && docker compose up -d --build  # Rebuild after code changes
git pull && docker compose down && docker compose up -d --build  # Deploy update

Modules

1. Reporting

Consolidated QC report search from Box.com and local database.

Features:

  • Job number search (single or comma-separated for multi-job)
  • Async search with real-time progress bar
  • Box reports saved locally for instant re-viewing (no re-fetch)
  • Previous Box Reports section with View/Delete
  • Dashboard with designer-friendly error display
  • Export: HTML and CSV (full or errors-only)

Workflow: Search job number -> Progress bar -> Dashboard with aggregated results

2. HM QC

AI-powered image quality control for marketing assets.

Profile: H&M Image Check (3 checks)

  • Filename Parse (30%) - Flexibly extracts country code, language, dimensions, campaign number from multiple H&M naming conventions (Display, DOOH, OOH, SOME STATIC, Social, POS)
  • Image Quality (40%) - AI visual assessment with strict text legibility rules; validates against campaign presentation guidelines when available
  • Price/Currency (30%) - Detects prices via LLM vision, then deterministic match against the attached Pricing Reference: currency symbol/format validated via _format lookup, actual prices validated via _prices lookup (exact numeric compare with 0.005 tolerance). Falls back to LLM-based campaign-sheet comparison only when the reference has no structured prices (e.g. legacy PDF references).

AI Quality Check evaluates:

  • Text & title legibility (CRITICAL - illegible text = automatic fail)
  • Language word validation (avoids false positives like "Rock" = German for skirt)
  • Campaign guideline compliance (typography, layout, copy, logo placement)
  • Image quality, color, composition
  • Logo and branding clarity

Features:

  • Single and batch file upload (up to 100 files)
  • Batch report grouping: reports grouped by upload batch with collapsible sections, batch stats, and "Download All" ZIP
  • Batch naming: batches display their job number (from configure step)
  • Delete batch: removes all reports, files, and thumbnails in a batch
  • Consolidated report: select multiple reports and download a single combined HTML with summary table + embedded individual reports
  • Asset thumbnails in report listings and embedded in HTML reports (base64, self-contained)
  • Sequential batch processing with gc.collect() between files for stable memory usage
  • LLM provider choice: OpenAI GPT-4o or Google Gemini 2.5 Flash
  • Two independent dropdowns at configure time: Campaign Presentation (for visual guideline checks) and Pricing Reference (for currency/price validation) — pick either, both, or neither
  • Previous QC Reports with View/Download/Delete on all pages (index, upload, results)
  • HTML report generation with per-check scoring
  • Usage tracking (tokens + estimated cost)

Workflow: Upload -> Configure (provider + campaign + pricing ref + job number) -> Execute -> Results

3. Video QC

AI-powered video quality control with direct video analysis and batch processing.

Checks:

  • Visual Quality (weight 50) - Language consistency + text legibility throughout the video
  • Censorship (weight 50) - Body coverage compliance (only for _CEN market files, skipped otherwise)
  • Price/Currency (weight 30, new in v2.4.0) - Detects prices across video frames via LLM, deterministic-validates currency + actual price against the attached Pricing Reference. Skipped if no reference attached, locale not parseable from filename, GEN/CEN markets, or no price visible.

Overall score is the weighted mean of non-skipped checks (so skipping any one check falls through cleanly).

How it works (Google Gemini — default):

  1. Uploads the video file directly to Google Gemini via genai.upload_file()
  2. Gemini processes the full video with temporal context (motion, transitions, audio)
  3. AI analyzes language consistency, text legibility, branding, and prices in a single pass per check
  4. Language check includes false-positive prevention (e.g., "Rock" = skirt in German)

How it works (OpenAI — fallback):

  1. Extracts 1 frame per second from the video
  2. Stitches frames into a labeled grid image
  3. Sends grid to GPT-4o for analysis (1 API call per check)

Features:

  • Default: Google Gemini direct video analysis (no frame extraction)
  • Fallback: OpenAI GPT-4o frame grid method
  • CEN market auto-detection from filename
  • Multi-file batch processing (new in v2.4.0): upload up to 50 videos, sequential processing with gc.collect() between files, batch results page with summary + per-file list + ZIP download
  • Previous Video QC Reports grouped by batch on the index page (collapsible sections, same pattern as HM QC)
  • Two independent dropdowns at configure: Campaign Presentation and Pricing Reference
  • Usage tracking

Workflow: Upload video(s) -> Configure (provider + campaign + pricing ref) -> Execute -> Results

  • Single file → /video-qc/results/<session_id> (single report)
  • Multiple files → /video-qc/results/batch/<session_id> (batch summary + per-file list)

4. Video Master Adot

Campaign-based master-to-adaptation video matching using Box.com integration.

How it works:

  1. User enters campaign name
  2. System searches Box for campaign folder, finds Global Masters and Regional Masters
  3. Preview shows: master count, countries, adaptation count
  4. Phase 1: Downloads each master temporarily, fingerprints it (~50KB), deletes video
  5. Phase 2: Downloads each adaptation temporarily, matches against fingerprints, deletes video
  6. Results: per-master adaptation mapping, unmatched items, match rate

Matching Engine (4-tier cascade):

  • Stage 0: Metadata filtering (80-95% reduction)
  • Tier 1: Perceptual hash matching
  • Tier 2: AKAZE feature verification
  • Tier 3: AI Vision fallback (smart triggering)

Storage: Only fingerprints (~50KB/master) stored permanently. Videos deleted after processing.

Box Folder Structure:

CAMPAIGNS/{campaign_name}/
├── Global Masters/          (various casing)
│   ├── DOOH/
│   ├── DS/
│   ├── OLV/
│   └── ... (video files with MASTER in name)
└── Regional Masters/        (various casing)
    ├── DE/ (country code folders)
    ├── FR/
    └── ...

5. Printer Check

CSV-to-PDF cross-referencing for print order validation. Ported from the CrossMatch desktop application.

What it does:

  1. User uploads a CSV order sheet and a ZIP file containing the PDF folder structure
  2. Filters CSV rows by selected geographic region and country groups
  3. Scans the PDF folder structure (multi-region or country-level layouts)
  4. Matches CSV filenames against actual PDF files
  5. Reports: matched, missing, and extra files with structural warnings

Features:

  • Auto-detects CSV delimiter (tab or comma)
  • Region and country group selection (EEU, CEU, etc.)
  • Campaign detection and filtering from filenames
  • Language column normalization (GEN files, KZ/MK locale handling)
  • Folder structure validation: misplaced GEN files, duplicate GEN, wrong country folders, files at wrong level
  • Results filtering by status (All, Matched, Missing, Extra)
  • XLSX export of filtered data
  • GEN asset priority: special handling for Root/GEN folder validation

Folder Layouts Supported:

  • Multi-Region: Root/EEU/PL/, Root/CEU/DE/, Root/GEN/
  • Country-Level: Root/PL/, Root/DE/, Root/GEN/

Workflow: Select region -> Upload CSV + PDF ZIP -> Process -> View results -> Export XLSX

6. Campaigns

Reference data layer consumed by both Image QC and Video QC. Holds two independent things:

Campaign Presentations (tied to a campaign_id)

  • PDF — creative guidelines with typography specs, layout rules, copy text, ratio-specific mockups. Parsed via LlamaParse (text + page images).
  • Excel — media plan / spec sheet parsed via openpyxl into structured text.
  • Multiple documents per campaign are supported and loaded together at QC time.
  • Linked to a specific campaign ID (e.g. 1022B, 1013A) typed at upload time.

Pricing References (standalone library, new in v2.4.0)

Independent uploadable documents — NOT tied to a campaign_id. Users pick one at QC configure time alongside (or instead of) a campaign presentation.

  • Excel Mastersheet — parsed deterministically with openpyxl, no LLM. Looks for:
    • MPC Prices sheet → flat list of {product_id, language, country, price, currency, product_name} entries (the authoritative source).
    • Regional sheets (AME, CEU, EEU, NEU, SEU, FRN, SHE, GCN, EAS, IN, BR…) → formatted prices per locale column, used to derive currency symbol, position, decimal_separator, thousands_separator.
    • Sheets matching OLD or COPY are skipped. Rows marked PRICE NOT PRESENT IN REPORT are skipped.
  • PDF — falls back to LlamaParse + LLM extraction for currency format metadata only (no _prices produced).

Stored shape in PricingReference.parsed_data_json:

{
  "_format": {"en-US": {"currency_code":"USD","symbol":"$","position":"before",...}, ...},
  "_prices": [{"product_id":"1334912002","language":"en-US","price":"49.99","currency":"USD",...}, ...]
}

Workflow

  1. Upload campaign presentation PDF for a campaign (e.g., 1013A).
  2. Upload the mastersheet as a Pricing Reference (give it a name, e.g. "1013A Mastersheet").
  3. On HM QC or Video QC configure, two independent dropdowns appear: Campaign Presentation and Pricing Reference. Pick either/both/neither.

Features

  • Multiple documents per campaign (guidelines + media plan)
  • Pricing references library — upload multiple, name them, delete independently
  • Auto-polling: status badges update in-place when parsing completes
  • View parsed content and page images (campaign presentations)
  • API endpoints:
    • /campaigns/api/list — campaign presentations for dropdown
    • /campaigns/api/<campaign_id> — specific campaign with parsed content
    • /campaigns/api/pricing/list — pricing references for dropdown
    • /campaigns/api/pricing/status/<id> — parse status for polling

Backwards compatibility

If storage/reference/global_pricing.json exists on first startup after upgrading from ≤v2.3.x and no PricingReference rows are present, it is auto-imported as a "Default (legacy global)" row so existing installs keep a valid reference attached. Users just pick it from the dropdown.

7. Usage Dashboard

API usage tracking across all tools.

Displays:

  • Summary cards: total API calls, tokens used, estimated cost (USD)
  • Breakdowns: by provider, model, tool, user
  • Recent API calls table with full details
  • Time filters: All Time, 30 Days, 7 Days, Today

Cost estimates based on per-model token pricing (GPT-4o, Gemini 2.5 Flash, etc.)


Configuration

Environment Variables (.env)

# Authentication
AUTH_USERS=admin:pbkdf2:sha256:600000$$salt$$hash

# Session
SESSION_COOKIE_PATH=/hm-ai-qc-report

# Box
BOX_CONFIG_PATH=config/box_config.json
BOX_REPORT_FOLDER_ID=133295752718
BOX_CAMPAIGNS_FOLDER_ID=156182880490

# Flask
SECRET_KEY=<generate-random-key>
FLASK_ENV=production

# Database (use absolute path for Docker)
DATABASE_URI=sqlite:////app/database/qc_platform.db

# LLM Providers
OPENAI_API_KEY=<your-key>
GOOGLE_API_KEY=<your-key>

Note: $$ in AUTH_USERS hash is required for Docker Compose (escapes $).


Architecture

Tech Stack

  • Backend: Flask 3.0, SQLAlchemy, Gunicorn (gthread workers)
  • Frontend: Bootstrap 5, Vanilla JS, Server-Sent Events
  • AI: OpenAI GPT-4o, Google Gemini 2.5 Flash (via google-generativeai)
  • Video: FFmpeg, OpenCV (AKAZE), Chromaprint
  • Storage: Box.com (JWT auth), SQLite
  • Deployment: Docker, Apache reverse proxy

Directory Structure

hm_ai_qc_report_tool/
├── app.py                    # Application factory
├── config.py                 # Configuration
├── Dockerfile                # Docker image
├── docker-compose.yml        # Docker services
├── deploy/                   # Deployment scripts & configs
│
├── core/                     # Shared infrastructure
│   ├── auth/                 # Session-based authentication
│   ├── models/               # Database models (QCReport, UsageLog, CampaignPresentation, PricingReference)
│   ├── services/             # LLM config, Box client
│   └── utils/                # Progress tracker, report parser
│
├── modules/
│   ├── hm_qc/               # HM QC (checks, executor, batch executor, profiles)
│   ├── video_qc/            # Video QC (executor, batch executor, price check)
│   ├── video_master/         # Video Master (matching engine, campaign matcher)
│   ├── printer_check/        # Printer Check (CSV parser, folder scanner, matcher)
│   ├── campaigns/            # Campaign presentations + pricing references library
│   ├── reporting/            # Reporting (aggregator, Box search, cache)
│   └── usage/                # Usage dashboard
│
├── templates/                # Shared templates (base.html, login.html)
├── static/                   # CSS, JavaScript
├── database/                 # SQLite database
└── storage/
    ├── reports/              # QC report HTML files
    ├── campaigns/            # Campaign presentation PDFs + page images
    ├── pricing_references/   # Pricing reference files (per-row dir)
    ├── thumbnails/           # Asset thumbnails
    └── reference/            # Legacy global_pricing.json (auto-imported on upgrade)

Security

  • Local username/password auth with PBKDF2/scrypt hashing
  • Session-based with before_request login enforcement
  • No hardcoded API keys (all from environment)
  • Docker container binds to 127.0.0.1 only (not exposed to internet)
  • HTTPS via Apache with wildcard SSL certificate
  • httpOnly, Secure, SameSite=Lax cookies

License

Proprietary - H&M Hennes & Mauritz AB