File size: 5,288 Bytes
b458f3d
 
 
 
 
 
 
 
 
 
19f9d48
 
b458f3d
 
a88e274
4ec2569
a88e274
 
 
 
4ec2569
b458f3d
 
4ec2569
 
b458f3d
 
 
 
4ec2569
b458f3d
 
 
4ec2569
 
 
 
 
 
 
 
 
 
 
a88e274
 
4ec2569
a88e274
 
 
 
 
 
 
4ec2569
b3f7149
 
 
 
 
4ec2569
b3f7149
 
 
 
 
 
 
 
 
 
 
 
 
 
4ec2569
a88e274
4ec2569
a88e274
4ec2569
a88e274
 
 
4ec2569
a88e274
4ec2569
 
19f9d48
4ec2569
19f9d48
 
14aaae8
 
b458f3d
 
 
 
14aaae8
c1d121b
14aaae8
b458f3d
 
 
 
 
 
c1d121b
14aaae8
b458f3d
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

class ImageAnalyzer:
    def analyze_site(self, site_data):
        """
        Analyzes the crawled site data for missing ALT text.
        :param site_data: Dict { page_url: [image_dicts] }
        :return: Dict containing summary and detailed report.
        """
        total_images = 0
        total_missing_alt = 0
        total_poor_quality = 0 # Track all poor quality images
        total_short_alt_legacy = 0 # Track only strictly short ones
        pages_report = []

        # Compile regex for filename patterns
        import re
        # Pattern 1: Common Camera Prefixes (e.g., DSC_001, IMG_1234, Screenshot-1)
        prefix_pattern = re.compile(r'^(dsc|img|pic|screenshot|photo)[\-_]?\d+', re.IGNORECASE)
        # Pattern 2: General Slug + Number (e.g., product-12345, item_01) - enforces no spaces
        slug_pattern = re.compile(r'^[a-z0-9\-_]+[-_]\d+$', re.IGNORECASE)
        
        for page_url, images in site_data.items():
            missing_images = []
            poor_quality_images = []
            
            for img in images:
                total_images += 1
                alt_text = img.get('alt')
                
                # 1. Strictly Missing
                if not alt_text or alt_text.strip() == "":
                    missing_images.append(img['src'])
                    total_missing_alt += 1
                    continue # Skip further checks if missing

                cleaned_alt = alt_text.strip()
                lower_alt = cleaned_alt.lower()
                word_count = len(cleaned_alt.split())
                
                is_poor = False
                reason = ""

                # 2. Advanced Poor Quality Checks
                
                # A) File Name / Extension Check (Highest Priority)
                if any(lower_alt.endswith(ext) for ext in ['.jpg', '.png', '.jpeg', '.webp', '.gif', '.svg']):
                    is_poor = True
                    reason = "Filename extension detected"
                elif prefix_pattern.search(cleaned_alt):
                    is_poor = True
                    reason = "Filename pattern detected (prefix)"
                elif slug_pattern.match(cleaned_alt):
                    is_poor = True
                    reason = "Filename pattern detected (slug)"

                # B) Generic / Filler Text (Smart check)
                # Flag if alt equals or contains generic terms.
                # Exception: "Brand Name Logo" (usually > 2 words or specific)
                # If word count is low (< 3) AND contains a generic term, it's likely poor.
                # If it is EXACTLY a generic term, it is definitely poor.
                generic_terms = {'image', 'photo', 'picture', 'logo', 'banner', 'icon', 'thumbnail', 'placeholder', 'img', 'spacer'}
                
                if not is_poor:
                    if lower_alt in generic_terms:
                         # Exact match found (e.g., "logo", "image") -> Bad
                         is_poor = True
                         reason = "Generic filler text"
                    else:
                        # Check partial match for short phrases (likely "My Logo", "Chart Image")
                        # If phrase is long (>= 3 words), it might be descriptive enough (e.g., "Eminent Tactiles Logo")
                        # So we only flag partial generic matches if word count is small (< 3)
                        has_generic = any(term in lower_alt.split() for term in generic_terms) 
                        if has_generic and word_count < 3:
                            is_poor = True
                            reason = "Generic filler text (partial match)"
                
                # C) Too Short / Sparse (Lowest Priority)
                if not is_poor:
                    if len(cleaned_alt) < 5:
                        is_poor = True
                        reason = "Too short (< 5 chars)"
                    elif word_count < 2:
                        # Single word check
                        is_poor = True
                        reason = "Too few words (needs >= 2)"

                if is_poor:
                    # Store with reason
                    poor_quality_images.append({'src': img['src'], 'alt': alt_text, 'reason': reason})
                    total_poor_quality += 1
            
            # User Intent: "short alt list and that count are add in poor not it show separately"
            # Action: Consolidate everything into 'poor_quality' and remove specific 'short_alt' reporting.
            
            pages_report.append({
                "page_url": page_url,
                "missing_alt_count": len(missing_images),
                "poor_quality_count": len(poor_quality_images), # Contains ALL poor images (Generic, Filename, Short)
                "images_without_alt": list(set(missing_images)),
                "images_with_poor_alt": poor_quality_images
            })

        return {
            "summary": {
                "total_pages_scanned": len(site_data),
                "total_images_found": total_images,
                "total_images_missing_alt": total_missing_alt,
                "total_images_poor_quality": total_poor_quality
            },
            "details": pages_report
        }