Files
justvitamin/app.py

196 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""JustVitamin × QuikCue — AI Content Engine
Live proposal site with real Gemini-powered demos."""
import os, json, time, traceback
from pathlib import Path
from flask import (Flask, render_template, jsonify, request,
send_from_directory, redirect)
from scraper import scrape_product, scrape_competitor
from ai_engine import (generate_asset_pack, competitor_xray, pdp_surgeon,
optimise_pdp_copy, generate_product_images)
app = Flask(__name__,
static_folder="static",
template_folder="templates")
GEN_DIR = Path(__file__).parent / "generated"
GEN_DIR.mkdir(exist_ok=True)
# ── Page routes ──────────────────────────────────────────────
@app.route("/")
def index():
return render_template("index.html")
@app.route("/dashboard")
@app.route("/dashboard/")
def dashboard():
return redirect("/dashboard/index.html")
@app.route("/dashboard/<path:filename>")
def dashboard_files(filename):
return send_from_directory("static/dashboard", filename)
@app.route("/proposal")
def proposal():
return send_from_directory("static/proposal", "index.html")
@app.route("/offer")
def offer():
return send_from_directory("static/offer", "index.html")
@app.route("/generated/<path:filename>")
def serve_generated(filename):
return send_from_directory(GEN_DIR, filename)
# ── API: Scrape ──────────────────────────────────────────────
@app.route("/api/scrape", methods=["POST"])
def api_scrape():
"""Scrape a JustVitamins product page."""
url = request.json.get("url", "").strip()
if not url:
return jsonify({"error": "No URL provided"}), 400
try:
data = scrape_product(url)
if not data.get("title"):
return jsonify({"error": "Could not find product. Check URL."}), 400
return jsonify(data)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
@app.route("/api/scrape-competitor", methods=["POST"])
def api_scrape_competitor():
"""Scrape any competitor product page."""
url = request.json.get("url", "").strip()
if not url:
return jsonify({"error": "No URL provided"}), 400
try:
data = scrape_competitor(url)
if not data.get("title"):
return jsonify({"error": "Could not extract product data."}), 400
return jsonify(data)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
# ── API: Demo A — 12-Asset Pack ──────────────────────────────
@app.route("/api/generate-pack", methods=["POST"])
def api_generate_pack():
"""Generate a full 12-asset marketing pack from product data."""
product = request.json
if not product:
return jsonify({"error": "No product data"}), 400
try:
t0 = time.time()
pack = generate_asset_pack(product)
pack["_generation_time"] = f"{time.time()-t0:.1f}s"
pack["_product_title"] = product.get("title", "")
return jsonify(pack)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
# ── API: Demo B — Competitor X-Ray ───────────────────────────
@app.route("/api/competitor-xray", methods=["POST"])
def api_competitor_xray():
"""Scrape competitor + run AI analysis."""
url = request.json.get("url", "").strip()
if not url:
return jsonify({"error": "No URL provided"}), 400
try:
t0 = time.time()
comp_data = scrape_competitor(url)
if not comp_data.get("title"):
return jsonify({"error": "Could not scrape competitor page."}), 400
analysis = competitor_xray(comp_data)
analysis["_scrape_data"] = {
"title": comp_data.get("title"),
"price": comp_data.get("price"),
"brand": comp_data.get("brand"),
"url": url,
}
analysis["_generation_time"] = f"{time.time()-t0:.1f}s"
return jsonify(analysis)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
# ── API: Demo C — PDP Surgeon ────────────────────────────────
@app.route("/api/pdp-surgeon", methods=["POST"])
def api_pdp_surgeon():
"""Rewrite PDP copy in a given style."""
data = request.json or {}
product = data.get("product")
style = data.get("style", "balanced")
if not product:
return jsonify({"error": "No product data"}), 400
try:
t0 = time.time()
result = pdp_surgeon(product, style)
result["_generation_time"] = f"{time.time()-t0:.1f}s"
return jsonify(result)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
# ── API: Full PDP Optimise ───────────────────────────────────
@app.route("/api/optimise", methods=["POST"])
def api_optimise():
"""Full PDP rewrite — scrape + AI copy."""
product = request.json
if not product:
return jsonify({"error": "No product data"}), 400
try:
t0 = time.time()
copy = optimise_pdp_copy(product)
copy["_generation_time"] = f"{time.time()-t0:.1f}s"
return jsonify(copy)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
# ── API: Image Generation ────────────────────────────────────
@app.route("/api/generate-images", methods=["POST"])
def api_generate_images():
"""Generate AI product images via Nano Banana / Pro."""
product = request.json
if not product:
return jsonify({"error": "No product data"}), 400
try:
t0 = time.time()
images = generate_product_images(product)
images["_generation_time"] = f"{time.time()-t0:.1f}s"
return jsonify(images)
except Exception as e:
traceback.print_exc()
return jsonify({"error": str(e)}), 500
# ── Health check ─────────────────────────────────────────────
@app.route("/api/health")
def health():
return jsonify({
"status": "ok",
"gemini_key_set": bool(os.environ.get("GEMINI_API_KEY")),
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5050, debug=True)