From 1c582ffcf439452e64a4e0201e7026f01601be2d Mon Sep 17 00:00:00 2001 From: nickviljoen Date: Sat, 21 Mar 2026 16:50:35 +0200 Subject: [PATCH] Batch 2: Simplify to single profile, fix multi-file batch execution - Replace 3 profiles with single "H&M Image Check" (dimension_check + image_quality) - Remove filename_parse check (pattern didn't match actual filenames) - Create DimensionCheck class for image dimension validation - Fix configure page to route multi-file uploads to batch endpoint - Auto-select single profile, show file list on configure page Co-Authored-By: Claude Opus 4.6 (1M context) --- modules/hm_qc/checks/__init__.py | 5 +- modules/hm_qc/checks/dimension_check.py | 86 ++++++++++++++++++++ modules/hm_qc/executor.py | 7 +- modules/hm_qc/profiles/profiles.yaml | 75 ++--------------- modules/hm_qc/templates/hm_qc/configure.html | 51 +++++++++--- 5 files changed, 140 insertions(+), 84 deletions(-) create mode 100644 modules/hm_qc/checks/dimension_check.py diff --git a/modules/hm_qc/checks/__init__.py b/modules/hm_qc/checks/__init__.py index 7a4248e..c08638b 100644 --- a/modules/hm_qc/checks/__init__.py +++ b/modules/hm_qc/checks/__init__.py @@ -3,8 +3,9 @@ # Base check class from .base_check import BaseCheck -# Sample checks +# Checks from .sample_filename_check import FilenameCheck from .sample_quality_check import QualityCheck +from .dimension_check import DimensionCheck -__all__ = ['BaseCheck', 'FilenameCheck', 'QualityCheck'] +__all__ = ['BaseCheck', 'FilenameCheck', 'QualityCheck', 'DimensionCheck'] diff --git a/modules/hm_qc/checks/dimension_check.py b/modules/hm_qc/checks/dimension_check.py new file mode 100644 index 0000000..8c4f182 --- /dev/null +++ b/modules/hm_qc/checks/dimension_check.py @@ -0,0 +1,86 @@ +""" +Dimension Check. + +Validates image dimensions and integrity using PIL. +""" +import os +from typing import Dict, Any +from PIL import Image +from .base_check import BaseCheck + + +class DimensionCheck(BaseCheck): + """Check image dimensions and validate integrity.""" + + # Minimum acceptable dimensions (in pixels) + MIN_WIDTH = 100 + MIN_HEIGHT = 100 + + def __init__(self, name: str = "dimension_check", weight: float = 50.0, config: Dict[str, Any] = None): + super().__init__(name, weight, config) + + def run(self, file_path: str, context: Dict[str, Any]) -> Dict[str, Any]: + """Run dimension validation on an image file.""" + try: + ext = os.path.splitext(file_path)[1].lower() + + if ext not in ['.jpg', '.jpeg', '.png', '.psd']: + return self._create_result( + status='skipped', + score=100.0, + message='Dimension check only applies to image files', + details={'file_type': ext} + ) + + img = Image.open(file_path) + width, height = img.size + mode = img.mode + fmt = img.format or ext.replace('.', '').upper() + + # Store in context for other checks + context['image_dimensions'] = {'width': width, 'height': height} + context['image_info'] = {'mode': mode, 'format': fmt} + + # Validate dimensions + issues = [] + if width < self.MIN_WIDTH: + issues.append(f'Width {width}px is below minimum {self.MIN_WIDTH}px') + if height < self.MIN_HEIGHT: + issues.append(f'Height {height}px is below minimum {self.MIN_HEIGHT}px') + + if issues: + return self._create_result( + status='failed', + score=30.0, + message=f'Image dimensions too small: {width}x{height}', + details={ + 'width': width, + 'height': height, + 'format': fmt, + 'mode': mode, + 'issues': issues + }, + recommendations=['Provide a higher resolution image'] + ) + + return self._create_result( + status='passed', + score=100.0, + message=f'Image dimensions valid: {width}x{height} ({fmt})', + details={ + 'width': width, + 'height': height, + 'format': fmt, + 'mode': mode, + 'megapixels': round((width * height) / 1_000_000, 2) + } + ) + + except Exception as e: + self.logger.error(f"Dimension check error: {e}") + return self._create_result( + status='error', + score=0.0, + message=f'Error reading image: {str(e)}', + details={'error': str(e)} + ) diff --git a/modules/hm_qc/executor.py b/modules/hm_qc/executor.py index 4b3b330..21043a0 100644 --- a/modules/hm_qc/executor.py +++ b/modules/hm_qc/executor.py @@ -11,7 +11,7 @@ import logging from datetime import datetime from typing import Dict, List, Any from .scoring import ScoringEngine -from .checks import FilenameCheck, QualityCheck +from .checks import FilenameCheck, QualityCheck, DimensionCheck from core.utils.progress_tracker import UnifiedProgressTracker from core.models.qc_report import QCReport from core.models.database import db @@ -126,10 +126,11 @@ class QCExecutor: checks = [] profile_checks = self.profile.get('checks', []) - # Map check names to classes (in real implementation, this would be dynamic) check_map = { 'filename_parse': FilenameCheck, - 'quality_check': QualityCheck + 'quality_check': QualityCheck, + 'image_quality': QualityCheck, + 'dimension_check': DimensionCheck } for check_config in profile_checks: diff --git a/modules/hm_qc/profiles/profiles.yaml b/modules/hm_qc/profiles/profiles.yaml index 3a1a575..eedf891 100644 --- a/modules/hm_qc/profiles/profiles.yaml +++ b/modules/hm_qc/profiles/profiles.yaml @@ -1,4 +1,4 @@ -# HM QC Profiles with Weighted Scoring +# HM QC Profiles # # Each profile defines: # - name: Profile display name @@ -6,81 +6,22 @@ # - checks: List of checks with weights and LLM configuration profiles: - standard_pdf: - name: "Standard PDF QC (Demo)" - description: "Demo profile with 2 sample checks" - checks: - - name: "filename_parse" - weight: 40 - enabled: true - llm_provider: null - description: "Validate H&M filename conventions" - - - name: "quality_check" - weight: 60 - enabled: true - llm_provider: "openai" - llm_model: "gpt-4o" - description: "AI-powered quality assessment" - - standard_image: - name: "Standard Image QC" + hm_image_check: + name: "H&M Image Check" description: "Quality checks for H&M image assets (JPG, PNG, PSD)" checks: - - name: "image_parse" - weight: 5 - enabled: true - llm_provider: null - description: "Parse image metadata and properties" - - - name: "filename_parse" - weight: 10 - enabled: true - llm_provider: null - description: "Validate H&M filename conventions" - - name: "dimension_check" - weight: 15 + weight: 50 enabled: true llm_provider: null - description: "Verify image dimensions match filename" + description: "Verify image dimensions and integrity" - name: "image_quality" - weight: 30 + weight: 50 enabled: true llm_provider: "openai" llm_model: "gpt-4o" - description: "Assess image quality and resolution" + description: "AI-powered image quality and legibility assessment" - - name: "censorship_check" - weight: 40 - enabled: true - llm_provider: "openai" - llm_model: "gpt-4o" - description: "Check body coverage requirements (CEN markets only)" - - quick_check: - name: "Quick Check" - description: "Fast validation of essential requirements only" - checks: - - name: "filename_parse" - weight: 30 - enabled: true - llm_provider: null - description: "Validate filename conventions" - - - name: "dimension_check" - weight: 35 - enabled: true - llm_provider: null - description: "Verify dimensions" - - - name: "language_validate" - weight: 35 - enabled: true - llm_provider: "openai" - llm_model: "gpt-4o" - description: "Validate language" - -# Note: Weights should sum to approximately 100 for each profile +# Note: Weights should sum to 100 for each profile # Higher weight = more important to overall score diff --git a/modules/hm_qc/templates/hm_qc/configure.html b/modules/hm_qc/templates/hm_qc/configure.html index 0d72397..fa63067 100644 --- a/modules/hm_qc/templates/hm_qc/configure.html +++ b/modules/hm_qc/templates/hm_qc/configure.html @@ -4,28 +4,39 @@ {% block content %}
-

Configure QC Checks

-

Select a QC profile and customize settings

+
+
+

Configure QC Checks

+

Review settings and start execution

+
+ + Back + +
- Select QC Profile + QC Settings
+ {% for profile_id, profile_data in profiles.items() %} + {% if loop.first %} +
{{ profile_data.description }}
+ {% endif %} + {% endfor %}
@@ -49,11 +60,18 @@
- - Session Info + + Files to Check
-

Session ID:
{{ session_id }}

+

{{ file_count }} file{{ 's' if file_count != 1 }} uploaded

+ {% if filenames %} +
    + {% for f in filenames %} +
  • {{ f }}
  • + {% endfor %} +
+ {% endif %}
@@ -64,6 +82,7 @@ {% block extra_scripts %}