from __future__ import annotations from typing import Any, Dict, List, Tuple from catalog_text_templates import ( RECOMMENDATION_TEMPLATES, STRENGTH_TEMPLATES, VERDICT_LEVELS, WEAKNESS_TEMPLATES, ) def clamp(v: float, lo: float, hi: float) -> float: return max(lo, min(hi, v)) def pick_top_unique(items: List[str], limit: int = 3) -> List[str]: out: List[str] = [] seen = set() for item in items: if item and item not in seen: out.append(item) seen.add(item) if len(out) >= limit: break return out def build_strength_keys( official_scores: Dict[str, float], diagnostic_metrics: Dict[str, int], ) -> List[str]: keys: List[str] = [] if official_scores.get("composition", 0.0) >= 4.2: keys.append("strong_composition") if diagnostic_metrics.get("hierarchy_strength", 0) >= 4: keys.append("clear_hierarchy") if diagnostic_metrics.get("focal_clarity", 0) >= 4: keys.append("clear_focal_point") if diagnostic_metrics.get("palette_control", 0) >= 4: keys.append("controlled_palette") if diagnostic_metrics.get("visual_impact", 0) >= 4: keys.append("strong_impact") if diagnostic_metrics.get("technical_quality", 0) >= 4: keys.append("good_technical_quality") if diagnostic_metrics.get("visual_clutter", 0) <= 2: keys.append("clean_presentation") if official_scores.get("typography", 0.0) >= 4.0: keys.append("readable_typography") if official_scores.get("message_clarity", 0.0) >= 4.0: keys.append("good_message_clarity") return pick_top_unique(keys, 3) def build_weakness_keys( official_scores: Dict[str, float], diagnostic_metrics: Dict[str, int], ) -> List[str]: keys: List[str] = [] if official_scores.get("composition", 0.0) <= 2.8: keys.append("weak_composition") if diagnostic_metrics.get("hierarchy_strength", 0) <= 2: keys.append("weak_hierarchy") if diagnostic_metrics.get("focal_clarity", 0) <= 2: keys.append("weak_focal_point") if diagnostic_metrics.get("palette_control", 0) <= 2: keys.append("weak_palette_control") if diagnostic_metrics.get("visual_impact", 0) <= 2: keys.append("low_visual_impact") if diagnostic_metrics.get("technical_quality", 0) <= 2: keys.append("low_technical_quality") if diagnostic_metrics.get("visual_clutter", 0) >= 4: keys.append("high_clutter") if official_scores.get("typography", 0.0) <= 3.0: keys.append("weak_typography") if official_scores.get("message_clarity", 0.0) <= 3.0: keys.append("weak_message_clarity") return pick_top_unique(keys, 3) def build_recommendation_keys( official_scores: Dict[str, float], diagnostic_metrics: Dict[str, int], pins: List[str], ) -> List[str]: keys: List[str] = [] if diagnostic_metrics.get("hierarchy_strength", 0) <= 3: keys.append("improve_hierarchy") if diagnostic_metrics.get("visual_clutter", 0) >= 4: keys.append("reduce_clutter") if diagnostic_metrics.get("focal_clarity", 0) <= 3: keys.append("improve_focal_point") if official_scores.get("typography", 0.0) <= 3.3: keys.append("improve_typography") if official_scores.get("color", 0.0) <= 3.2: keys.append("improve_palette") if official_scores.get("message_clarity", 0.0) <= 3.5: keys.append("improve_message_clarity") if official_scores.get("quality", 0.0) <= 3.3: keys.append("improve_technical_quality") if "credits_box" in pins or "small_footer_info" in pins: keys.append("improve_footer_block") return pick_top_unique(keys, 3) def render_texts_from_keys( keys: List[str], template_map: Dict[str, str], ) -> List[str]: return [template_map[k] for k in keys if k in template_map] def compute_verdict_level( primary_label: str, primary_confidence: str, diagnostic_score: float, ) -> str: if primary_label == "good" and diagnostic_score >= 4.3: return "strong" if primary_label == "good" and diagnostic_score >= 3.5: return "good" if primary_label == "uncertain": return "uncertain" if primary_label == "bad" and diagnostic_score <= 2.7: return "weak" return "mixed" def build_verdict_summary( *, verdict_level: str, primary_label: str, diagnostic_score: float, tags: List[str], strengths: List[str], weaknesses: List[str], ) -> str: level_text = VERDICT_LEVELS.get(verdict_level, "Смешанный") tone_bits: List[str] = [] if "minimal" in tags: tone_bits.append("минималистичной подачей") if "clean" in tags: tone_bits.append("чистой подачей") if "cinematic" in tags: tone_bits.append("атмосферной подачей") if "editorial" in tags: tone_bits.append("редакционной структурой") tone_text = "" if tone_bits: tone_text = " с " + ", ".join(tone_bits[:2]) if primary_label == "good": return f"{level_text} постер{tone_text}, который в целом производит положительное визуальное впечатление." if primary_label == "bad": return f"{level_text} постер, которому пока не хватает ясности и собранности восприятия." return f"{level_text} постер: в нем есть сильные стороны, но общий сигнал пока неоднозначен." def build_verdict_takeaway( strengths: List[str], weaknesses: List[str], recommendations: List[str], ) -> str: if recommendations: first = recommendations[0] if first.endswith("."): return first return first + "." if weaknesses: return "Основной резерв роста связан с улучшением читаемости и структуры." if strengths: return "Текущую визуальную логику стоит сохранить и доработать точечно." return "Нужна дополнительная проверка композиции, ясности сообщения и визуальной иерархии." def build_structured_report( *, official_scores: Dict[str, float], diagnostic_metrics: Dict[str, int], tags: List[str], pins: List[str], primary_label: str, primary_confidence: str, diagnostic_score: float, ) -> Dict[str, Any]: strength_keys = build_strength_keys(official_scores, diagnostic_metrics) weakness_keys = build_weakness_keys(official_scores, diagnostic_metrics) recommendation_keys = build_recommendation_keys(official_scores, diagnostic_metrics, pins) strengths = render_texts_from_keys(strength_keys, STRENGTH_TEMPLATES) weaknesses = render_texts_from_keys(weakness_keys, WEAKNESS_TEMPLATES) recommendations = render_texts_from_keys(recommendation_keys, RECOMMENDATION_TEMPLATES) verdict_level = compute_verdict_level( primary_label=primary_label, primary_confidence=primary_confidence, diagnostic_score=diagnostic_score, ) verdict_summary = build_verdict_summary( verdict_level=verdict_level, primary_label=primary_label, diagnostic_score=diagnostic_score, tags=tags, strengths=strengths, weaknesses=weaknesses, ) verdict_takeaway = build_verdict_takeaway( strengths=strengths, weaknesses=weaknesses, recommendations=recommendations, ) return { "tags": tags, "pins": pins, "strengths": strengths, "weaknesses": weaknesses, "recommendations": recommendations, "verdict": { "level": verdict_level, "summary": verdict_summary, "takeaway": verdict_takeaway, }, "debug_keys": { "strength_keys": strength_keys, "weakness_keys": weakness_keys, "recommendation_keys": recommendation_keys, }, }