HASHFOR_BACKEND / app.py
moizshah956's picture
Update app.py
97ffd70 verified
Raw
History Blame Contribute Delete
18 kB
import gradio as gr
import asyncio
import subprocess
import sys
import os
from datetime import datetime
os.environ.setdefault("PLAYWRIGHT_BROWSERS_PATH", "/home/user/.cache/ms-playwright")
def install_playwright_browsers():
marker = os.path.join(os.environ["PLAYWRIGHT_BROWSERS_PATH"], ".installed")
if os.path.exists(marker):
print("βœ… Playwright browsers already installed, skipping.")
return
try:
print("πŸ“¦ Installing Playwright browsers...")
subprocess.check_call(
[sys.executable, "-m", "playwright", "install", "chromium"],
)
os.makedirs(os.environ["PLAYWRIGHT_BROWSERS_PATH"], exist_ok=True)
open(marker, "w").close()
print("βœ… Playwright browsers installed successfully.")
except subprocess.CalledProcessError as e:
print(f"❌ Playwright install failed with code {e.returncode}")
except Exception as e:
print(f"❌ Unexpected error during install: {e}")
install_playwright_browsers()
try:
import spaces
HAS_SPACES = True
except ImportError:
HAS_SPACES = False
if HAS_SPACES:
@spaces.GPU
def dummy_gpu():
pass
from seo_analyzer import run_seo_analysis_fastapi
from ai_visibility import run_ai_visibility_analysis
# ---- Helper functions to format output ----
def format_seo_result(result):
if isinstance(result, tuple):
data, csv_path = result
if not data:
return "No data returned."
output = f"## SEO Analysis Results\n"
output += f"**Pages analyzed:** {len(data)}\n"
scores = [p.get('seo_score', 0) for p in data]
avg = sum(scores) / len(scores) if scores else 0
output += f"**Average SEO Score:** {avg:.1f}/100\n\n"
# Generate detailed strengths and issues based on page metrics
detailed_strengths = []
detailed_issues = []
for page in data:
url = page.get('url', '')
# Title
title_len = len(page.get('title', ''))
if 50 <= title_len <= 60:
detailed_strengths.append(f"- Strong title length ({title_len} chars) on {url}")
elif title_len < 30 or title_len > 70:
detailed_issues.append(f"- Title too {'short' if title_len < 30 else 'long'} ({title_len} chars) on {url}")
# Meta description
meta_len = len(page.get('meta_description', ''))
if 120 <= meta_len <= 160:
detailed_strengths.append(f"- Good meta description length ({meta_len} chars) on {url}")
elif meta_len > 0 and (meta_len < 70 or meta_len > 170):
detailed_issues.append(f"- Meta description length ({meta_len} chars) suboptimal on {url}")
# H1
h1 = page.get('h1_count', 0)
if h1 == 1:
detailed_strengths.append(f"- Exactly one H1 on {url}")
elif h1 == 0:
detailed_issues.append(f"- Missing H1 on {url}")
elif h1 > 1:
detailed_issues.append(f"- Multiple H1s ({h1}) on {url}")
# Word count
wc = page.get('word_count', 0)
if wc >= 800:
detailed_strengths.append(f"- Good word count ({wc}) on {url}")
elif wc < 300:
detailed_issues.append(f"- Low word count ({wc}) on {url}")
# Alt tags
total_img = page.get('total_images', 0)
missing_alt = page.get('missing_alt_tags', 0)
if total_img > 0 and missing_alt == 0:
detailed_strengths.append(f"- All images have alt text on {url}")
elif total_img > 0 and missing_alt > 0:
detailed_issues.append(f"- {missing_alt} images missing alt text on {url}")
# Schema
schema = page.get('schema_types', '')
if schema and schema != "No schema found":
detailed_strengths.append(f"- Schema detected ({schema}) on {url}")
else:
detailed_issues.append(f"- No schema found on {url}")
# Readability
readability = page.get('readability_score', 0)
if readability >= 50:
detailed_strengths.append(f"- Good readability score ({readability}) on {url}")
elif readability < 30:
detailed_issues.append(f"- Poor readability ({readability}) on {url}")
if detailed_strengths:
output += "### SEO Strengths (detailed)\n"
output += "\n".join(detailed_strengths) + "\n\n"
if detailed_issues:
output += "### SEO Issues (detailed)\n"
output += "\n".join(detailed_issues) + "\n\n"
# Per-page data
for i, page in enumerate(data, 1):
output += f"### Page {i}: {page.get('url', '')}\n"
output += f"- Score: {page.get('seo_score', 0)}/100\n"
output += f"- Title: {page.get('title', 'No title')}\n"
output += f"- Word Count: {page.get('word_count', 0)}\n"
output += f"- H1: {page.get('h1_count', 0)}, H2: {page.get('h2_count', 0)}, H3: {page.get('h3_count', 0)}\n"
output += f"- Images: {page.get('total_images', 0)} (missing alt: {page.get('missing_alt_tags', 0)})\n"
output += f"- Internal/External links: {page.get('internal_links', 0)}/{page.get('external_links', 0)}\n"
output += f"- Readability: {page.get('readability_score', 0)}\n"
output += f"- Grammar Errors: {page.get('grammar_errors', 0)}\n"
output += f"- Canonical Tag: {'Yes' if page.get('canonical_tag') else 'No'}\n"
output += f"- OpenGraph Tags: {page.get('opengraph_tags', 0)}\n"
output += f"- Twitter Cards: {page.get('twitter_tags', 0)}\n"
output += f"- Robots Meta: {page.get('robots_meta', 'none')}\n"
output += f"- Viewport: {'Yes' if page.get('viewport_present') else 'No'}\n"
output += f"- Schema Types: {page.get('schema_types', 'none')}\n"
output += f"- Text/HTML Ratio: {page.get('text_to_html_ratio', 0)}%\n"
output += f"- Load Time: {page.get('load_time', 0)}s\n"
output += f"- Meta Description: {page.get('meta_description', '')}\n"
output += f"- Heading Order: {page.get('heading_order', '')}\n"
if page.get('ai_suggestions'):
output += f"- AI Suggestions: {page['ai_suggestions'][:200]}...\n"
output += "\n"
return output
else:
return f"❌ Error: {result.get('message', 'Unknown error')}"
def format_ai_result(result):
if result.get('status') == 'error':
return f"❌ Error: {result.get('message', 'Unknown error')}"
output = f"## AI Visibility / Readiness Analysis\n"
output += f"**URL:** {result.get('url', '')}\n"
output += f"**Pages analyzed:** {result.get('pages_analyzed', 0)}\n"
output += f"**Overall AI Readiness Score:** {result.get('ai_readiness_score', 0)}/100\n"
output += f"**Page types detected:** {result.get('page_type_breakdown', {})}\n\n"
cat_scores = result.get('category_scores', {})
if cat_scores:
output += "### Category Scores\n"
for k, v in cat_scores.items():
output += f"- {k.replace('_score', '').replace('_', ' ').title()}: {v if v is not None else 'N/A'}\n"
output += "\n"
previews = result.get('results_preview', [])
if previews:
output += "### Per-Page Details\n"
for p in previews:
output += f"**URL:** {p.get('url', '')}\n"
output += f"- Page Type: {p.get('page_type', 'unknown')} (conf: {p.get('page_type_confidence', 0):.2f})\n"
output += f"- Readiness Score: {p.get('ai_readiness_score', 0)}/100\n"
output += f"- Topic Clarity: {p.get('topic_clarity', 0)}\n"
output += f"- Content Completeness: {p.get('content_completeness', 0)}\n"
output += f"- Entity Clarity: {p.get('entity_clarity', 'N/A')}\n"
output += f"- Freshness: {p.get('freshness_status', 'unknown')}\n\n"
# Detailed issues and strengths (per page) from backend
issues = result.get('issues', [])
strengths = result.get('strengths', [])
if issues:
output += "### Detailed Issues (per page)\n"
for issue in issues:
output += f"- {issue.get('title')} (Severity: {issue.get('severity')}) on {issue.get('page')}\n"
output += f" Explanation: {issue.get('explanation')}\n"
if issue.get('recommended_fix'):
output += f" Fix: {issue.get('recommended_fix')}\n"
if strengths:
output += "### Detailed Strengths (per page)\n"
for strength in strengths:
output += f"- {strength.get('title')} on {strength.get('page')}\n"
output += f" Detail: {strength.get('detail')}\n"
return output
# ---- Async analysis wrappers ----
async def analyze_seo_async(url, max_pages, max_concurrent, use_ai):
result = await run_seo_analysis_fastapi(
base_url=url,
max_pages=int(max_pages),
use_ai=use_ai,
max_concurrent=int(max_concurrent)
)
return format_seo_result(result)
async def analyze_ai_async(url, max_pages, max_concurrent, use_ai):
result = await run_ai_visibility_analysis(
base_url=url,
max_pages=int(max_pages),
max_concurrent=int(max_concurrent),
use_ai=use_ai
)
return format_ai_result(result)
# ---- Gradio Interface ----
with gr.Blocks(title="SEO & AI Visibility Analyzer") as demo:
gr.Markdown("# πŸš€ SEO & AI Visibility Analysis Tool")
gr.Markdown("Enter a website URL to analyze its SEO health and AI search readiness.")
with gr.Row():
with gr.Column(scale=2):
url_input = gr.Textbox(label="Website URL", placeholder="https://example.com", value="https://example.com")
with gr.Column(scale=1):
max_pages_input = gr.Number(label="Max Pages", value=3, minimum=1, maximum=20, step=1)
with gr.Column(scale=1):
max_concurrent_input = gr.Number(label="Concurrent Browsers", value=1, minimum=1, maximum=5, step=1)
with gr.Column(scale=1):
use_ai_check = gr.Checkbox(label="Enable AI Suggestions", value=True)
with gr.Row():
seo_btn = gr.Button("πŸ” Analyze SEO", variant="primary")
ai_btn = gr.Button("πŸ€– Analyze AI Visibility", variant="secondary")
output = gr.Markdown(label="Results")
seo_btn.click(
fn=analyze_seo_async,
inputs=[url_input, max_pages_input, max_concurrent_input, use_ai_check],
outputs=output
)
ai_btn.click(
fn=analyze_ai_async,
inputs=[url_input, max_pages_input, max_concurrent_input, use_ai_check],
outputs=output
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)
# import gradio as gr
# import asyncio
# import subprocess
# import sys
# import os
# from datetime import datetime
# os.environ.setdefault("PLAYWRIGHT_BROWSERS_PATH", "/home/user/.cache/ms-playwright")
# def install_playwright_browsers():
# """Install Playwright Chromium browsers – works on Hugging Face Spaces."""
# marker = os.path.join(os.environ["PLAYWRIGHT_BROWSERS_PATH"], ".installed")
# if os.path.exists(marker):
# print("βœ… Playwright browsers already installed, skipping.")
# return
# try:
# print("πŸ“¦ Installing Playwright browsers...")
# subprocess.check_call(
# [sys.executable, "-m", "playwright", "install", "chromium"],
# )
# os.makedirs(os.environ["PLAYWRIGHT_BROWSERS_PATH"], exist_ok=True)
# open(marker, "w").close()
# print("βœ… Playwright browsers installed successfully.")
# except subprocess.CalledProcessError as e:
# print(f"❌ Playwright install failed with code {e.returncode}")
# except Exception as e:
# print(f"❌ Unexpected error during install: {e}")
# install_playwright_browsers()
# try:
# import spaces
# HAS_SPACES = True
# except ImportError:
# HAS_SPACES = False
# if HAS_SPACES:
# @spaces.GPU
# def dummy_gpu():
# pass # This makes ZeroGPU happy
# from seo_analyzer import run_seo_analysis_fastapi
# from ai_visibility import run_ai_visibility_analysis
# # ---- Helper functions to format output ----
# def format_seo_result(result):
# if isinstance(result, tuple):
# data, csv_path = result
# if not data:
# return "No data returned."
# output = f"## SEO Analysis Results\n"
# output += f"**Pages analyzed:** {len(data)}\n"
# scores = [p.get('seo_score', 0) for p in data]
# avg = sum(scores) / len(scores) if scores else 0
# output += f"**Average SEO Score:** {avg:.1f}/100\n\n"
# for i, page in enumerate(data, 1):
# output += f"### Page {i}: {page.get('url', '')}\n"
# output += f"- Score: {page.get('seo_score', 0)}/100\n"
# output += f"- Title: {page.get('title', 'No title')}\n"
# output += f"- Word Count: {page.get('word_count', 0)}\n"
# output += f"- H1: {page.get('h1_count', 0)}, H2: {page.get('h2_count', 0)}, H3: {page.get('h3_count', 0)}\n"
# output += f"- Images: {page.get('total_images', 0)} (missing alt: {page.get('missing_alt_tags', 0)})\n"
# output += f"- Internal/External links: {page.get('internal_links', 0)}/{page.get('external_links', 0)}\n"
# if page.get('ai_suggestions'):
# output += f"- AI Suggestions: {page['ai_suggestions'][:200]}...\n"
# output += "\n"
# return output
# else:
# return f"❌ Error: {result.get('message', 'Unknown error')}"
# def format_ai_result(result):
# if result.get('status') == 'error':
# return f"❌ Error: {result.get('message', 'Unknown error')}"
# output = f"## AI Visibility / Readiness Analysis\n"
# output += f"**URL:** {result.get('url', '')}\n"
# output += f"**Pages analyzed:** {result.get('pages_analyzed', 0)}\n"
# output += f"**Overall AI Readiness Score:** {result.get('ai_readiness_score', 0)}/100\n"
# output += f"**Page types detected:** {result.get('page_type_breakdown', {})}\n\n"
# cat_scores = result.get('category_scores', {})
# if cat_scores:
# output += "### Category Scores\n"
# for k, v in cat_scores.items():
# output += f"- {k.replace('_score', '').replace('_', ' ').title()}: {v if v is not None else 'N/A'}\n"
# output += "\n"
# previews = result.get('results_preview', [])
# if previews:
# output += "### Per-Page Details\n"
# for p in previews:
# output += f"**URL:** {p.get('url', '')}\n"
# output += f"- Page Type: {p.get('page_type', 'unknown')} (conf: {p.get('page_type_confidence', 0):.2f})\n"
# output += f"- Readiness Score: {p.get('ai_readiness_score', 0)}/100\n"
# output += f"- Topic Clarity: {p.get('topic_clarity', 0)}\n"
# output += f"- Content Completeness: {p.get('content_completeness', 0)}\n"
# output += f"- Entity Clarity: {p.get('entity_clarity', 'N/A')}\n"
# output += f"- Freshness: {p.get('freshness_status', 'unknown')}\n\n"
# return output
# # ---- Async analysis wrappers (Gradio will handle async functions) ----
# async def analyze_seo_async(url, max_pages, max_concurrent, use_ai):
# result = await run_seo_analysis_fastapi(
# base_url=url,
# max_pages=int(max_pages),
# use_ai=use_ai,
# max_concurrent=int(max_concurrent)
# )
# return format_seo_result(result)
# async def analyze_ai_async(url, max_pages, max_concurrent, use_ai):
# result = await run_ai_visibility_analysis(
# base_url=url,
# max_pages=int(max_pages),
# max_concurrent=int(max_concurrent),
# use_ai=use_ai
# )
# return format_ai_result(result)
# # ---- Gradio Interface ----
# with gr.Blocks(title="SEO & AI Visibility Analyzer") as demo:
# gr.Markdown("# πŸš€ SEO & AI Visibility Analysis Tool")
# gr.Markdown("Enter a website URL to analyze its SEO health and AI search readiness.")
# with gr.Row():
# with gr.Column(scale=2):
# url_input = gr.Textbox(label="Website URL", placeholder="https://example.com", value="https://example.com")
# with gr.Column(scale=1):
# max_pages_input = gr.Number(label="Max Pages", value=3, minimum=1, maximum=20, step=1)
# with gr.Column(scale=1):
# max_concurrent_input = gr.Number(label="Concurrent Browsers", value=1, minimum=1, maximum=5, step=1)
# with gr.Column(scale=1):
# use_ai_check = gr.Checkbox(label="Enable AI Suggestions", value=True)
# with gr.Row():
# seo_btn = gr.Button("πŸ” Analyze SEO", variant="primary")
# ai_btn = gr.Button("πŸ€– Analyze AI Visibility", variant="secondary")
# output = gr.Markdown(label="Results")
# seo_btn.click(
# fn=analyze_seo_async,
# inputs=[url_input, max_pages_input, max_concurrent_input, use_ai_check],
# outputs=output
# )
# ai_btn.click(
# fn=analyze_ai_async,
# inputs=[url_input, max_pages_input, max_concurrent_input, use_ai_check],
# outputs=output
# )
# if __name__ == "__main__":
# demo.launch(server_name="0.0.0.0", server_port=7860)