last_edit / server /action_plan_generator.py
Moharek
Deploy Moharek GEO Platform
a74b879
"""
Action Plan Generator
Automatically generates actionable SEO and GEO recommendations
"""
from typing import Dict, List
import json
def generate_action_plan_from_audit(audit: Dict) -> Dict:
"""
Generate comprehensive action plan from audit results
Returns prioritized, actionable tasks
"""
pages = audit.get('pages', [])
geo_score = audit.get('geo_score', 0)
ai_visibility = audit.get('ai_visibility', {})
actions = []
# 1. Critical Issues (High Priority)
actions.extend(_generate_critical_actions(audit, pages))
# 2. SEO Issues (High Priority)
actions.extend(_generate_seo_actions(pages))
# 3. Content Issues (Medium Priority)
actions.extend(_generate_content_actions(pages))
# 4. AI Visibility Issues (Medium Priority)
actions.extend(_generate_ai_visibility_actions(ai_visibility, geo_score))
# 5. Technical Issues (Low Priority)
actions.extend(_generate_technical_actions(pages))
# Sort by priority
priority_order = {'critical': 0, 'high': 1, 'medium': 2, 'low': 3}
actions.sort(key=lambda x: priority_order.get(x['priority'], 4))
# Calculate completion stats
total_actions = len(actions)
critical_count = len([a for a in actions if a['priority'] == 'critical'])
high_count = len([a for a in actions if a['priority'] == 'high'])
return {
'ok': True,
'actions': actions,
'summary': {
'total': total_actions,
'critical': critical_count,
'high': high_count,
'medium': len([a for a in actions if a['priority'] == 'medium']),
'low': len([a for a in actions if a['priority'] == 'low'])
},
'estimated_time': _estimate_total_time(actions),
'estimated_impact': _estimate_total_impact(actions)
}
def _generate_critical_actions(audit: Dict, pages: List[Dict]) -> List[Dict]:
"""Generate critical priority actions"""
actions = []
# Check for pages with 0 words
empty_pages = [p for p in pages if p.get('word_count', 0) < 100]
if empty_pages:
actions.append({
'id': 'critical_empty_content',
'type': 'content',
'priority': 'critical',
'title': f'محتوى فارغ في {len(empty_pages)} صفحة',
'description': 'صفحات بدون محتوى كافٍ لن تظهر في نتائج البحث أو الذكاء الاصطناعي',
'task': f'أضف محتوى تفصيلي (600+ كلمة) لـ {len(empty_pages)} صفحة',
'impact': 'عالي جداً',
'effort': 'متوسط',
'timeline': f'{len(empty_pages) * 2} ساعة',
'pages_affected': [p.get('url') for p in empty_pages[:5]]
})
# Check for missing Schema
pages_without_schema = [p for p in pages if not _has_schema(p)]
if len(pages_without_schema) == len(pages):
actions.append({
'id': 'critical_no_schema',
'type': 'technical',
'priority': 'critical',
'title': 'لا يوجد Schema Markup',
'description': 'Schema Markup ضروري للظهور في نتائج الذكاء الاصطناعي',
'task': 'أضف Organization و FAQPage Schema لجميع الصفحات',
'impact': 'عالي جداً',
'effort': 'منخفض',
'timeline': '2 ساعة',
'code_example': 'استخدم schema_generator.py'
})
return actions
def _generate_seo_actions(pages: List[Dict]) -> List[Dict]:
"""Generate SEO-related actions"""
actions = []
# Missing meta descriptions
pages_without_meta = [p for p in pages if not _has_meta_description(p)]
if pages_without_meta:
actions.append({
'id': 'seo_meta_descriptions',
'type': 'seo',
'priority': 'high',
'title': f'Meta Description مفقود في {len(pages_without_meta)} صفحة',
'description': 'Meta descriptions تحسن نسبة النقر في نتائج البحث',
'task': f'أضف meta description مُحسّن لـ {len(pages_without_meta)} صفحة',
'impact': 'عالي',
'effort': 'منخفض',
'timeline': f'{len(pages_without_meta) * 15} دقيقة',
'pages_affected': [p.get('url') for p in pages_without_meta[:5]],
'tool': 'استخدم meta_generator.py'
})
# Missing H1 tags
pages_without_h1 = [p for p in pages if not _has_h1(p)]
if pages_without_h1:
actions.append({
'id': 'seo_h1_tags',
'type': 'seo',
'priority': 'high',
'title': f'H1 مفقود في {len(pages_without_h1)} صفحة',
'description': 'H1 tag ضروري لمحركات البحث لفهم موضوع الصفحة',
'task': f'أضف H1 واضح ومُحسّن لـ {len(pages_without_h1)} صفحة',
'impact': 'عالي',
'effort': 'منخفض',
'timeline': f'{len(pages_without_h1) * 10} دقيقة',
'pages_affected': [p.get('url') for p in pages_without_h1[:5]]
})
# Duplicate titles
titles = [p.get('title', '') for p in pages]
duplicate_titles = [t for t in titles if titles.count(t) > 1]
if duplicate_titles:
actions.append({
'id': 'seo_duplicate_titles',
'type': 'seo',
'priority': 'medium',
'title': f'{len(set(duplicate_titles))} عنوان مكرر',
'description': 'العناوين المكررة تضعف أداء SEO',
'task': 'اجعل كل عنوان صفحة فريد ووصفي',
'impact': 'متوسط',
'effort': 'منخفض',
'timeline': '1 ساعة'
})
return actions
def _generate_content_actions(pages: List[Dict]) -> List[Dict]:
"""Generate content-related actions"""
actions = []
# Low content density
thin_pages = [p for p in pages if 100 < p.get('word_count', 0) < 300]
if thin_pages:
actions.append({
'id': 'content_thin_pages',
'type': 'content',
'priority': 'medium',
'title': f'محتوى ضعيف في {len(thin_pages)} صفحة',
'description': 'الصفحات ذات المحتوى القليل تحصل على ترتيب أقل',
'task': f'وسّع المحتوى إلى 600+ كلمة في {len(thin_pages)} صفحة',
'impact': 'متوسط',
'effort': 'متوسط',
'timeline': f'{len(thin_pages) * 1.5} ساعة',
'pages_affected': [p.get('url') for p in thin_pages[:5]]
})
# No entities (for AI visibility)
pages_without_entities = [p for p in pages if len(p.get('entities', [])) < 3]
if len(pages_without_entities) > len(pages) * 0.5:
actions.append({
'id': 'content_entities',
'type': 'content',
'priority': 'medium',
'title': 'محتوى غير منظم - لا كيانات معرفية',
'description': 'الذكاء الاصطناعي يحتاج كيانات واضحة (أسماء، أماكن، منتجات)',
'task': 'أضف أسماء محددة، أرقام، وأماكن في المحتوى',
'impact': 'عالي',
'effort': 'متوسط',
'timeline': '3 ساعات',
'example': 'بدلاً من "نقدم خدمات ممتازة" → "نقدم خدمات SEO في الرياض منذ 2020"'
})
return actions
def _generate_ai_visibility_actions(ai_visibility: Dict, geo_score: int) -> List[Dict]:
"""Generate AI visibility improvement actions"""
actions = []
if geo_score < 50:
actions.append({
'id': 'ai_low_geo_score',
'type': 'ai_visibility',
'priority': 'high',
'title': f'درجة GEO منخفضة ({geo_score}/100)',
'description': 'موقعك غير مرئي في محركات الذكاء الاصطناعي',
'task': 'نفّذ جميع التوصيات لرفع درجة GEO',
'impact': 'عالي جداً',
'effort': 'عالي',
'timeline': '2-4 أسابيع',
'steps': [
'أضف محتوى تفصيلي 600+ كلمة',
'أضف Schema Markup',
'أضف FAQ Schema',
'حسّن الكيانات المعرفية',
'أضف روابط خارجية موثوقة'
]
})
mentions = ai_visibility.get('mentions', 0)
if mentions == 0:
actions.append({
'id': 'ai_no_mentions',
'type': 'ai_visibility',
'priority': 'high',
'title': 'لا يوجد ذكر في الذكاء الاصطناعي',
'description': 'علامتك التجارية غير معروفة لمحركات الذكاء الاصطناعي',
'task': 'بناء حضور رقمي قوي',
'impact': 'عالي',
'effort': 'عالي',
'timeline': '4-8 أسابيع',
'steps': [
'انشر محتوى عالي الجودة بانتظام',
'احصل على روابط من مواقع موثوقة',
'شارك في المنتديات والمجتمعات',
'أنشئ ملفات تعريف في الدلائل الرئيسية'
]
})
return actions
def _generate_technical_actions(pages: List[Dict]) -> List[Dict]:
"""Generate technical SEO actions"""
actions = []
# Check for mobile-friendliness
actions.append({
'id': 'technical_mobile',
'type': 'technical',
'priority': 'medium',
'title': 'فحص التوافق مع الموبايل',
'description': 'تأكد من أن الموقع متجاوب على جميع الأجهزة',
'task': 'اختبر الموقع على أجهزة مختلفة وأصلح المشاكل',
'impact': 'عالي',
'effort': 'متوسط',
'timeline': '1-2 أيام',
'tool': 'استخدم mobile_checker.py'
})
# Check page speed
actions.append({
'id': 'technical_speed',
'type': 'technical',
'priority': 'low',
'title': 'تحسين سرعة الموقع',
'description': 'المواقع السريعة تحصل على ترتيب أفضل',
'task': 'ضغط الصور، تفعيل التخزين المؤقت، تصغير CSS/JS',
'impact': 'متوسط',
'effort': 'متوسط',
'timeline': '2-3 أيام'
})
return actions
def _has_schema(page: Dict) -> bool:
"""Check if page has Schema markup"""
html = page.get('html', '')
return 'application/ld+json' in html or '@type' in html
def _has_meta_description(page: Dict) -> bool:
"""Check if page has meta description"""
html = page.get('html', '')
import re
return bool(re.search(r'<meta\s+name=["\']description["\']', html, re.IGNORECASE))
def _has_h1(page: Dict) -> bool:
"""Check if page has H1 tag"""
headings = page.get('headings', [])
for h in headings:
if isinstance(h, dict) and h.get('level') == 1:
return True
elif isinstance(h, str) and h.startswith('h1'):
return True
return False
def _estimate_total_time(actions: List[Dict]) -> str:
"""Estimate total time to complete all actions"""
total_hours = 0
for action in actions:
timeline = action.get('timeline', '')
if 'ساعة' in timeline or 'hour' in timeline:
import re
match = re.search(r'(\d+(?:\.\d+)?)', timeline)
if match:
total_hours += float(match.group(1))
elif 'دقيقة' in timeline or 'minute' in timeline:
import re
match = re.search(r'(\d+)', timeline)
if match:
total_hours += float(match.group(1)) / 60
elif 'يوم' in timeline or 'day' in timeline:
import re
match = re.search(r'(\d+)', timeline)
if match:
total_hours += float(match.group(1)) * 8
elif 'أسبوع' in timeline or 'week' in timeline:
import re
match = re.search(r'(\d+)', timeline)
if match:
total_hours += float(match.group(1)) * 40
if total_hours < 8:
return f'{int(total_hours)} ساعة'
elif total_hours < 40:
return f'{int(total_hours / 8)} يوم'
else:
return f'{int(total_hours / 40)} أسبوع'
def _estimate_total_impact(actions: List[Dict]) -> str:
"""Estimate total impact of completing all actions"""
high_impact = len([a for a in actions if a.get('impact') in ['عالي جداً', 'عالي', 'high']])
if high_impact > 5:
return 'عالي جداً - تحسين كبير متوقع'
elif high_impact > 2:
return 'عالي - تحسين ملحوظ متوقع'
else:
return 'متوسط - تحسين تدريجي'