nat-ad / app.py
ibombonato's picture
chore: remove "natura-cliente" in template front end
30d4e20 verified
import gradio as gr
import os
import base64
from dotenv import load_dotenv
from image_generator_tool import GenerateImageTool
from social_media_crew import SocialMediaCrew
from utils_tools import GetImageUrlTool
load_dotenv()
def clean_env_vars():
os.environ.pop("OPENAI_API_KEY", None)
os.environ.pop("NATURA_API_TOKEN", None)
os.environ.pop("OPENAI_BASE_URL", None)
os.environ.pop("OPENAI_MODEL_NAME", None)
js_share_logic = """
async (image_url) => {
if (!image_url) {
console.error("Share button clicked, but no image URL found.");
alert("No image to share. Please generate an image first.");
return;
}
try {
const response = await fetch(image_url);
const blob = await response.blob();
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = async function() {
const base64data = reader.result;
const parts = base64data.split(';base64,');
const contentType = parts[0].split(':')[1];
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
const newBlob = new Blob([uInt8Array], { type: contentType });
const fileName = `shared_image_${new Date().getTime()}.png`;
const file = new File([newBlob], fileName, { type: contentType });
if (navigator.share && navigator.canShare({ files: [file] })) {
await navigator.share({
files: [file],
title: "Image",
text: "Shared from app.",
});
} else {
alert("Sharing not supported on this browser.");
}
};
reader.onerror = function(error) {
console.error("Error converting blob to data URL:", error);
alert("An error occurred while preparing image for sharing.");
};
} catch (error) {
if (error.name === 'AbortError') {
console.log("Share dialog cancelled by user.");
} else {
console.error("Error sharing:", error);
alert("An error occurred while trying to share.");
}
}
}
"""
# --- Gradio Interface ---
def generate_ad(product_url: str, store_name: str, main_cupom: str, main_cupom_discount_percentage: float, cupom_1: str, original_price: float, discounted_price: float, openai_api_key: str, natura_api_token: str, openai_base_url: str, openai_model_name: str):
yield gr.update(interactive=False, value="Generating..."), gr.Markdown(value="⏳ Generating ad... Please wait."), gr.Image(label="Product Image", show_label=True, visible=False, width=200, height=200)
if not openai_api_key or not natura_api_token or not openai_model_name or not openai_base_url:
yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value="Please configure your API keys in the settings section below.")
return
image_url = None
if "natura" in product_url:
image_url = GetImageUrlTool()._run(product_url)
print(f"Extracted image URL: {image_url}")
original_price = original_price if original_price is not None else 0
discounted_price = discounted_price if discounted_price is not None else 0
social_media_crew = SocialMediaCrew(openai_api_key, natura_api_token, openai_base_url, openai_model_name)
result = social_media_crew.run_crew(product_url, store_name, main_cupom, main_cupom_discount_percentage, cupom_1, original_price, discounted_price)
if result == "INVALID_URL":
yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value="❌ The provided URL is invalid or the product page could not be found.")
elif result == "MISSING_PRODUCT_INFO":
yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value="⚠️ Could not extract all required product information from the URL. Please check the URL or try a different one.")
else:
yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value=result.raw), gr.Image(value=image_url, visible=image_url is not None)
with gr.Blocks() as demo:
gr.Markdown("# 🚀 Social Media Ad Generator")
gr.Markdown("Enter a product URL to generate a social media ad.")
with gr.Tab("Generate Ad"):
url_input = gr.Textbox(label="Product URL", placeholder="Enter product URL here...")
store_name_input = gr.Textbox(label="Store Name (e.g., O Boticário)", placeholder="Enter store name...")
main_cupom_input = gr.Textbox(label="Main Cupom (e.g., PRIMEIRACOMPRA)", value="MINHALOJATAON")
main_cupom_discount_percentage_input = gr.Number(label="Main Cupom Discount Percentage (e.g., 20 for 20%)", value=15, minimum=0, maximum=100)
cupom_1_input = gr.Textbox(label="Cupom 1 (e.g., AMIGO15)", placeholder="Enter first coupon code...")
original_price_input = gr.Number(label="Original Price (Optional)", value=0, minimum=0)
discounted_price_input = gr.Number(label="Discounted Price (Optional)", value=0, minimum=0)
with gr.Row():
generate_ad_button = gr.Button("Generate Ad")
clear_button = gr.Button("Clear")
with gr.Row():
ad_output = gr.Markdown(label="Your Generated Ad", show_copy_button=True)
ad_image_output = gr.Image(label="Product Image", show_label=False, visible=True, width=200, height=200)
with gr.Tab("Fragrantica"):
gr.Markdown("### 👃 Fragrantica Website Analyzer")
fragrantica_url_input = gr.Textbox(label="Fragrantica Product URL", placeholder="Enter Fragrantica product URL here...")
analyze_fragrantica_button = gr.Button("Analyze Fragrantica Product")
fragrantica_output = gr.Markdown(label="Fragrantica Analysis Report")
with gr.Tab("Images"):
gr.Markdown("### 🖼️ Generate Promotional Image")
with gr.Row():
with gr.Column():
image_product_url_input = gr.Textbox(label="Product Image URL", placeholder="Enter product image URL...")
image_product_name_input = gr.Textbox(label="Product Name", placeholder="Enter product name...")
image_original_price_input = gr.Number(label="Original Price", placeholder="Enter original price...")
image_final_price_input = gr.Number(label="Final Price", placeholder="Enter final price...")
image_coupon_code_input = gr.Textbox(label="Coupon Code", placeholder="Enter coupon code...")
template_selection = gr.Radio(
choices=[
"outros",
"outros_sem_cupom",
"natura",
"avon",
"lidi_promo",
],
value="outros",
label="Template Selection"
)
gen_image_btn = gr.Button("Generate Image")
with gr.Column():
image_output = gr.Image(label="Generated Image", height=500, type="filepath", interactive=False, show_share_button=True)
base64_debug_output = gr.Textbox(label="Base64 Debug Output", visible=True, interactive=False)
share_button = gr.Button("🚀 Share Image", interactive=False)
with gr.Tab("Settings"):
gr.Markdown("### ⚙️ API Key Settings")
gr.Markdown("Enter your API keys below. These will be used for the current session.")
openai_key_input = gr.Textbox(label="OPENAI_API_KEY", type="password", value=os.getenv("OPENAI_API_KEY", ""))
natura_token_input = gr.Textbox(label="NATURA_API_TOKEN", type="password", value=os.getenv("NATURA_API_TOKEN", ""))
openai_base_url_input = gr.Textbox(label="OPENAI_BASE_URL", value=os.getenv("OPENAI_BASE_URL", "https://llm-openai-ibombonato.openai.azure.com/openai/deployments/gpt-4.1-mini/chat/completions?api-version=2025-01-01-preview"))
openai_model_name_input = gr.Textbox(label="OPENAI_MODEL_NAME", value=os.getenv("OPENAI_MODEL_NAME", "azure/gpt-4.1-mini"))
clean_env_vars()
# No save button needed as keys are passed directly
gr.Markdown("API keys are used directly from these fields when you click 'Generate Ad'. They are not saved persistently.")
def clear_fields():
return "", 0, 0
def analyze_fragrantica_url(url, openai_api_key, natura_api_token, openai_base_url, openai_model_name):
yield "⏳ Analyzing Fragrantica product... Please wait." # Loading message
if not openai_api_key or not openai_model_name or not openai_base_url:
yield "Please configure your API keys in the settings section below."
return
from fragrantica_crew import FragranticaCrew
fragrantica_crew = FragranticaCrew(openai_api_key, openai_base_url, openai_model_name)
report = fragrantica_crew.kickoff(url=url)
if report == "SCRAPING_FAILED":
yield "❌ Scraping failed. The website could not be accessed or parsed. Please check the URL or try again later."
return
yield report.raw
def generate_image(product_image_url, product_name, original_price, final_price, coupon_code, template_name):
tool = GenerateImageTool()
original_price_str = f"{original_price:.2f}".replace('.', ',')
final_price_str = f"{final_price:.2f}".replace('.', ',')
yield gr.update(interactive=False, value="Generating..."), None, gr.update(interactive=False)
image_path = tool._run(
product_image_url=product_image_url,
product_name=product_name,
original_price=original_price_str,
final_price=final_price_str,
coupon_code=coupon_code,
template_name=template_name
)
yield gr.update(interactive=True, value="Generate Image"), image_path, gr.update(interactive=True)
def process_image_for_sharing(image_path):
if image_path is None:
return ""
try:
with open(image_path, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read()).decode("utf-8")
data_url = f"data:image/png;base64,{encoded_string}"
return data_url
except Exception as e:
print(f"Error processing image for sharing: {e}")
return ""
generate_ad_button.click(generate_ad,
inputs=[url_input, store_name_input, main_cupom_input, main_cupom_discount_percentage_input, cupom_1_input, original_price_input, discounted_price_input, openai_key_input, natura_token_input, openai_base_url_input, openai_model_name_input],
outputs=[generate_ad_button, ad_output, ad_image_output])
clear_button.click(clear_fields,
inputs=[],
outputs=[url_input, original_price_input, discounted_price_input])
analyze_fragrantica_button.click(analyze_fragrantica_url,
inputs=[fragrantica_url_input, openai_key_input, natura_token_input, openai_base_url_input, openai_model_name_input],
outputs=fragrantica_output)
gen_image_btn.click(generate_image,
inputs=[image_product_url_input, image_product_name_input, image_original_price_input, image_final_price_input, image_coupon_code_input, template_selection],
outputs=[gen_image_btn, image_output, share_button])
share_button.click(fn=process_image_for_sharing,
inputs=[image_output],
outputs=[base64_debug_output])
base64_debug_output.change(fn=None,
inputs=[base64_debug_output],
js=js_share_logic)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)