"""Professional GEO Report Generator — client-ready HTML + PDF.""" import json from pathlib import Path from datetime import datetime def build_html_report(job_dir: str) -> str: p = Path(job_dir) audit = {} analysis = {} recs = {} for fname, target in [('audit.json', 'audit'), ('analysis.json', 'analysis'), ('recommendations.json', 'recs')]: path = p / fname if path.exists(): try: data = json.loads(path.read_text(encoding='utf-8')) if target == 'audit': audit = data elif target == 'analysis': analysis = data elif target == 'recs': recs = data except Exception: pass pages = audit.get('pages', []) org = audit.get('org_name') or (pages[0].get('title') if pages else 'Website') url = audit.get('url') or (pages[0].get('url') if pages else '') geo = analysis.get('geo_score') or {} score = geo.get('score', 0) status = geo.get('status', 'N/A') breakdown = geo.get('breakdown', {}) actions = recs.get('actions', []) if isinstance(recs, dict) else [] per_page = recs.get('per_page', []) if isinstance(recs, dict) else [] date_str = datetime.utcnow().strftime('%Y-%m-%d') score_color = '#10b981' if score >= 75 else '#f59e0b' if score >= 40 else '#ef4444' def bar(val, max_val=20, color='#00f2ff'): pct = min(100, int((val / max_val) * 100)) return f'
' # Issues summary total_issues = sum(len(p.get('issues', [])) for p in per_page) total_pages = len(pages) # Per-page rows page_rows = '' for pg in per_page[:20]: issues = pg.get('issues', []) color = '#ef4444' if len(issues) > 1 else '#f59e0b' if issues else '#10b981' icon = '🔴' if len(issues) > 1 else '🟡' if issues else '🟢' page_rows += f'''
{pg.get("title") or pg.get("url","")}
{pg.get("url","")}
{icon} {"، ".join(issues) if issues else "✅ لا مشاكل"} ''' # Action items action_rows = '' for i, a in enumerate(actions[:10], 1): text = a.get('text', a) if isinstance(a, dict) else a priority = a.get('priority', 'MEDIUM') if isinstance(a, dict) else 'MEDIUM' p_color = '#ef4444' if priority == 'HIGH' else '#f59e0b' if priority == 'MEDIUM' else '#10b981' action_rows += f''' {i} {priority} {text} ''' html = f''' تقرير GEO — {org}
{score}%

تقرير GEO — {org}

{url}

تاريخ التقرير: {date_str}  ·  الحالة: {status}

الصفحات المحللة: {total_pages}  ·  المشاكل المكتشفة: {total_issues}

📊 تفصيل درجة GEO

{''.join(f"""
{label} {val}/20
{bar(val)}
""" for label, val in [ ('جودة العناوين', breakdown.get('headings', 0)), ('كثافة المحتوى', breakdown.get('density', 0)), ('الكيانات الدلالية', breakdown.get('entities', 0)), ('أسئلة FAQ', breakdown.get('faq', 0)), ('الظهور في الذكاء الاصطناعي', breakdown.get('ai_visibility', 0)), ])}

💡 خطة العمل ({len(actions)} توصية)

{'

لا توجد توصيات — شغّل تحليل الذكاء الاصطناعي أولاً.

' if not actions else f''' {action_rows}
#الأولويةالإجراء المطلوب
'''}

🔍 تحليل الصفحات ({total_pages} صفحة)

{'

لا توجد بيانات صفحات.

' if not per_page else f''' {page_rows}
الصفحةالحالةالمشاكل
'''}
{'

🤖 الظهور في الذكاء الاصطناعي

' + _render_ai_vis(audit.get('ai_visibility', {})) + '
' if audit.get('ai_visibility') else ''}
''' return html def _render_ai_vis(ai_vis: dict) -> str: if not ai_vis or not ai_vis.get('enabled'): reason = ai_vis.get('reason', 'بيانات الظهور غير متاحة — أضف مفتاح Perplexity أو OpenAI') if ai_vis else 'غير مفعّل' return f'

{reason}

' results = ai_vis.get('results', []) rows = ''.join(f''' {r.get("query","")} {'✅ موجود' if r.get('mentioned') else '❌ غائب'} ''' for r in results) return f'{rows}
الاستعلامالنتيجة
' def try_render_pdf(html: str, out_path: Path) -> bool: try: from weasyprint import HTML HTML(string=html).write_pdf(str(out_path)) return True except Exception: return False