import gradio as gr
import os
import time
from collections import defaultdict
from fastapi import Request, Response
from pdf_parser import extract_text_from_pdf
from analyzer import analyze_cv
from report_generator import generate_pdf_report
from storage import check_license_key
from webhook import handle_stripe_webhook
# ─── Konfigurace ────────────────────────────────────────────────
STRIPE_SINGLE = os.environ.get("STRIPE_LINK_SINGLE", "#")
STRIPE_PRO = os.environ.get("STRIPE_LINK_PRO", "#")
SANDBOX_PASSWORD = os.environ.get("SANDBOX_PASSWORD", "admin") # Výchozí heslo pro test
IS_SANDBOX = os.environ.get("STRIPE_MODE", "sandbox") == "sandbox"
# ─── Rate limiting ───────────────────────────────────────────────
request_log = defaultdict(list)
def check_rate_limit(session_id: str) -> bool:
now = time.time()
request_log[session_id] = [t for t in request_log[session_id] if now - t < 3600]
if len(request_log[session_id]) >= 3:
return False
request_log[session_id].append(now)
return True
# ─── Hlavní analýza ─────────────────────────────────────────────
def run_analysis(cv_file, cv_text_input, job_text, lang, license_key, session_id):
cv_text = ""
if cv_file is not None:
cv_text = extract_text_from_pdf(cv_file)
if not cv_text and cv_text_input:
cv_text = cv_text_input.strip()
if not cv_text:
return "❌ Nahraj CV nebo vlož text.", None, free_upgrade_html(), gr.update(visible=False)
if not job_text or len(job_text) < 50:
return "❌ Vlož popis pozice (alespoň 50 znaků).", None, free_upgrade_html(), gr.update(visible=False)
license_info = check_license_key(license_key)
is_pro = license_info is not None and license_info.get("active")
if not is_pro:
if not check_rate_limit(session_id):
return "⚠️ Limit 3 analýzy/hodinu vyčerpán. Upgraduj nebo počkej.", None, upgrade_html(), gr.update(visible=False)
analysis = analyze_cv(cv_text, job_text, language=lang)
if "error" in analysis:
return f"❌ {analysis['error']}", None, "", gr.update(visible=False)
score = analysis.get("match_score", 0)
emoji = "🟢" if score >= 75 else "🟡" if score >= 50 else "🔴"
# Sestavení hlavního výstupu
md = f"## {emoji} Match Score: {score}/100\n{analysis.get('score_explanation', '')}\n\n---\n\n"
md += "### 🚀 Kariérní doporučení & Úroveň\n"
md += f"{analysis.get('career_recommendations', 'Nebylo vygenerováno.')}\n\n---\n\n"
md += "### 🎯 Tvoje 3 nejdůležitější akce\n"
for i, a in enumerate(analysis.get("top_3_actions", []), 1):
md += f"\n**{i}.** {a}"
md += "\n\n---\n\n### 🔑 Chybějící klíčová slova\n"
for kw in analysis.get("missing_keywords", []):
if isinstance(kw, dict):
md += f"\n- **{kw.get('keyword', '')}** — {kw.get('why_it_matters', '')}"
else:
md += f"\n- {kw}"
md += "\n\n---\n\n### ✍️ Co posílit (přepiš vlastními slovy)\n"
for sec in analysis.get("weak_sections", []):
if isinstance(sec, dict):
md += f"\n> *\"{sec.get('original_phrase', '')}\"*\n"
md += f"- **Problém:** {sec.get('problem', '')}\n"
md += f"- **Co sdělit:** {sec.get('what_to_convey_instead', '')}\n"
md += f"- **Směr:** {sec.get('example_direction', '')}\n"
# Rozbalovací sekce
md += "\n\n---\n\n"
md += "⚠️ ATS varování (Klikni pro rozbalení)
\n\n"
for w in analysis.get("ats_warnings", []):
md += f"- {w}\n"
md += "💼 LinkedIn headline nápady (Klikni pro rozbalení)
\n\n"
for h in analysis.get("linkedin_headline_ideas", []):
md += f"- {h}\n"
md += "
🚀 Free plán: 3 analýzy/hodinu
Upgraduj pro neomezený přístup, historii hledání a čisté PDF bez watermarku.
Jednorázová analýza — $2.99 Pro plán — $9.99/měsíc⏳ Limit dosažen
3 analýzy za hodinu vyčerpány. Upgraduj nebo počkej hodinu.
Upgrade na Pro — $9.99/měsícUpload your CV. Paste the job. Know exactly what to fix — in your own words.