JeCabrera commited on
Commit
559f68e
·
verified ·
1 Parent(s): 731c1ef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +458 -455
app.py CHANGED
@@ -1,455 +1,458 @@
1
- from dotenv import load_dotenv
2
- import streamlit as st
3
- import os
4
- import google.generativeai as genai
5
- import random
6
- from streamlit import session_state as state
7
- from formulas import email_formulas # Updated import statement
8
- from angles import angles
9
-
10
- # Cargar las variables de entorno
11
- load_dotenv()
12
-
13
- # Configurar la API de Google
14
- genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
15
-
16
- # Fórmulas con ejemplos y explicaciones
17
- # email_formulas dictionary has been moved to formulas/email_formulas.py
18
-
19
- # Cambiar el nombre de la función
20
- def generate_emails(target_audience, product, temperature, selected_formula, selected_angle, file_content="", image_parts=None, is_image=False, emotion="", desired_action="", creative_idea=""):
21
- # Crear la configuración del modelo
22
- generation_config = {
23
- "temperature": temperature,
24
- "top_p": 0.65,
25
- "top_k": 360,
26
- "max_output_tokens": 8196,
27
- }
28
-
29
- model = genai.GenerativeModel(
30
- model_name="gemini-2.0-flash",
31
- generation_config=generation_config,
32
- )
33
-
34
- # Definir las instrucciones de formato una sola vez
35
- format_instructions = """
36
- FORMAT RULES:
37
- - Each email must have a clear and attractive subject line
38
- - The email body must be persuasive and emotional
39
- - Include a clear call to action
40
- - Add a professional signature
41
- - Separate each email with a dividing line
42
- - Do not include greetings like 'Hello [Name]'
43
- - Make sure that the postscripts (P.D.) are smaller and more discrete than the main body
44
- """
45
-
46
- # Incluir las instrucciones del sistema en el prompt principal
47
- system_prompt = f"""You are a world-class direct response copywriter trained by Gary Halbert, Gary Bencivenga, and David Ogilvy.
48
-
49
- You have helped many marketers before me persuade their clients through emotional email sequences.
50
- Your task is to create a 5-email sequence that makes my [buyer persona] feel [emotion] about my [product/service] and convince them to register/take [desired action].
51
-
52
- {format_instructions}
53
-
54
- IMPORTANT:
55
- - Each email must be unique and memorable
56
- - Avoid clichés and generalities
57
- - Maintain a persuasive but credible tone
58
- - Adapt language to the target audience
59
- - Focus on transformative benefits"""
60
-
61
- # Iniciar el prompt con las instrucciones del sistema
62
- email_instruction = f"{system_prompt}\n\n"
63
-
64
- # Añadir instrucciones para la idea creativa si existe
65
- if creative_idea:
66
- email_instruction += f"""
67
- CREATIVE CONCEPT:
68
- Use the following creative concept as the central theme for all emails in the sequence:
69
- "{creative_idea}"
70
-
71
- CREATIVE CONCEPT INSTRUCTIONS:
72
- 1. This concept should be the unifying theme across all emails
73
- 2. Use it as a metaphor or analogy throughout the sequence
74
- 3. Develop different aspects of this concept in each email
75
- 4. Make sure the concept naturally connects to the product benefits
76
- 5. The concept should make the emails more memorable and engaging
77
- """
78
-
79
- # Añadir contenido del archivo si existe
80
- if file_content:
81
- email_instruction += f"""
82
- REFERENCE CONTENT:
83
- Carefully analyze the following content as a reference for generating emails:
84
- {file_content[:3000]}
85
-
86
- ANALYSIS INSTRUCTIONS:
87
- 1. Extract key information about the product or service mentioned
88
- 2. Identify the tone, style, and language used
89
- 3. Detect any data about the target audience or customer avatar
90
- 4. Look for benefits, features, or pain points mentioned
91
- 5. Use relevant terms, phrases, or concepts from the content
92
- 6. Maintain consistency with the brand identity or main message
93
- 7. Adapt the emails to resonate with the provided content
94
-
95
- IMPORTANT COMBINATIONS:
96
- """
97
- # Updated conditions for specific input combinations
98
- if product and not target_audience:
99
- email_instruction += f"""- FILE + PRODUCT: You have a reference document and product ({product}). Create emails that highlight this specific product's benefits and features using insights from the document. Extract audience information from the document to better target the emails.
100
- """
101
- elif target_audience and not product:
102
- email_instruction += f"""- FILE + TARGET AUDIENCE: You have a reference document and target audience ({target_audience}). Create emails tailored to this specific audience using language and concepts from the document. Identify products or services from the document that would appeal to this audience.
103
- """
104
- elif product and target_audience:
105
- email_instruction += f"""- PRODUCT + TARGET AUDIENCE: You have both product ({product}) and target audience ({target_audience}). Create emails that connect this specific product with this specific audience, using insights from the document to strengthen the connection.
106
- """
107
-
108
- email_instruction += """
109
- IMPORTANT: Naturally integrate the elements found in the content with the selected formula and angle.
110
- """
111
-
112
- # Preparar instrucciones específicas del ángulo una sola vez
113
- angle_instructions = ""
114
- if selected_angle != "NINGUNO":
115
- angle_instructions = f"""
116
- MAIN ANGLE: {selected_angle}
117
- SPECIFIC ANGLE INSTRUCTIONS:
118
- {angles[selected_angle]["instruction"]}
119
-
120
- IMPORTANT: The angle {selected_angle} must be applied as a "style layer" over the formula structure:
121
- 1. Keep the base structure of the formula intact
122
- 2. Apply the tone and style of the {selected_angle} angle
123
- 3. Ensure each element of the formula reflects the angle
124
- 4. The angle affects "how" it's said, not "what" is said
125
-
126
- SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
127
- """
128
- for example in angles[selected_angle]["examples"]:
129
- angle_instructions += f"- {example}\n"
130
-
131
- # Añadir las instrucciones del ángulo al prompt principal
132
- email_instruction += angle_instructions
133
-
134
- # Dentro de la función, actualizar el prompt para incluir emoción y acción deseada
135
- email_instruction += (
136
- f"\nYour task is to create 5 persuasive emails for {target_audience} "
137
- f"that evoke {emotion} and convince them to {desired_action} about {product}. "
138
- )
139
-
140
- # Usar la variable angle_instructions para determinar si hay un ángulo seleccionado
141
- if angle_instructions:
142
- email_instruction += f"IMPORTANT: Each email MUST follow the {selected_angle} angle clearly and consistently.\n\n"
143
-
144
- email_instruction += (
145
- f"Avoid obvious mentions of {product} and focus on generating genuine interest"
146
- )
147
-
148
- if angle_instructions:
149
- email_instruction += f" using the selected angle"
150
-
151
- email_instruction += ".\n\n"
152
-
153
- # Agregar ejemplos de la fórmula
154
- examples_to_use = selected_formula['examples'][:min(5, len(selected_formula['examples']))]
155
-
156
- email_instruction += "FORMULA EXAMPLES TO STUDY (DO NOT COPY DIRECTLY):\n"
157
- for i, example in enumerate(examples_to_use, 1):
158
- email_instruction += f"{i}. {example}\n"
159
-
160
- # Consolidar todas las instrucciones específicas en un solo bloque
161
- specific_instructions = [
162
- "DO NOT copy the examples directly - create fresh, original content",
163
- f"Follow the logical STRUCTURE and SEQUENCE of the formula for {target_audience} and {product}",
164
- "Maintain the same persuasive techniques and psychological triggers",
165
- "Create a cohesive sequence where emails reference each other naturally",
166
- "Use the examples only as inspiration for the underlying structure and flow",
167
- "If an email references a previous one (like 'As I mentioned yesterday'), ensure this is logically consistent",
168
- "Be creative with stories, analogies, and examples while maintaining the formula's effectiveness"
169
- ]
170
-
171
- email_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
172
- for i, instruction in enumerate(specific_instructions, 1):
173
- email_instruction += f"{i}. {instruction}\n"
174
-
175
- # Añadir instrucciones específicas sobre la temperatura creativa
176
- email_instruction += f"\nCREATIVITY LEVEL: {temperature}. Higher values mean more creative and original content.\n\n"
177
-
178
- email_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
179
-
180
- # Consolidar las instrucciones finales en un solo bloque
181
- final_instructions = [
182
- "Follow the logical structure but not the exact wording",
183
- "Be creative and original with your content",
184
- "Maintain the sequence flow between emails"
185
- ]
186
-
187
- # Añadir instrucciones específicas para el ángulo si es necesario
188
- if selected_angle != "NINGUNO":
189
- final_instructions.extend([
190
- "Apply the angle as a 'style layer'",
191
- "Maintain coherence between formula and angle",
192
- "Ensure each email reflects both elements"
193
- ])
194
-
195
- email_instruction += "\nFINAL REMINDER:\n"
196
- for i, instruction in enumerate(final_instructions, 1):
197
- email_instruction += f"{i}. {instruction}\n"
198
-
199
- # Simplify the final instruction to clearly specify 5 emails
200
- email_instruction += f"\nGENERATE NOW:\nCreate 5 CREATIVE emails that follow the logical structure but with original content.\n"
201
-
202
- # Modificar la forma de enviar el mensaje según si hay imagen o no
203
- message_parts = [email_instruction]
204
-
205
- # Add the image to the message parts if it exists
206
- if is_image and image_parts:
207
- message_parts.append(image_parts)
208
- instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown, drawing inspiration from the provided image."
209
- else:
210
- instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown."
211
-
212
- # Simplificar las instrucciones finales para evitar redundancia
213
- instruction_text += " Do not include explanations, only the emails. IMPORTANT: Do not include greetings like 'Hello [Name]' and make sure that the postscripts (P.D.) are smaller and more discrete than the main body of the email, using a lighter format."
214
-
215
- # Create the chat session with the message parts
216
- chat_session = model.start_chat(
217
- history=[
218
- {
219
- "role": "user",
220
- "parts": message_parts,
221
- },
222
- ]
223
- )
224
-
225
- # Enviar el mensaje con las instrucciones
226
- response = chat_session.send_message(instruction_text)
227
-
228
- return response.text
229
-
230
- # Define the clean_response_text function before it's used
231
- def clean_response_text(text):
232
- """Remove extra spaces and normalize whitespace in the response text"""
233
- import re
234
- # Replace multiple newlines with just two
235
- text = re.sub(r'\n{3,}', '\n\n', text)
236
- # Remove leading/trailing whitespace
237
- text = text.strip()
238
- return text
239
-
240
- # Configurar la interfaz de usuario con Streamlit
241
- st.set_page_config(
242
- page_title="Email Composer",
243
- layout="wide",
244
- menu_items={}, # Empty dict removes the three-dot menu
245
- initial_sidebar_state="expanded"
246
- )
247
-
248
- # Add custom CSS to reduce margins
249
- st.markdown("""
250
- <style>
251
- .main > div {
252
- padding-top: 1rem;
253
- }
254
- </style>
255
- """, unsafe_allow_html=True)
256
-
257
- # Leer el contenido del archivo manual.md
258
- with open("manual.md", "r", encoding="utf-8") as file:
259
- manual_content = file.read()
260
-
261
- # Mostrar el contenido del manual en el sidebar
262
- st.sidebar.markdown(manual_content)
263
-
264
- # Load CSS from file
265
- with open("styles/main.css", "r") as f:
266
- css = f.read()
267
-
268
- # Apply the CSS
269
- st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
270
-
271
- # Centrar el título y el subtítulo
272
- st.markdown("<h1 style='text-align: center;'>Generador de Emails</h1>", unsafe_allow_html=True)
273
- st.markdown("<h4 style='text-align: center;'>Transforma tu marketing con emails persuasivos que convierten. Esta aplicación es tu arma secreta para crear emails emocionales de respuesta directa que impulsan a la acción.</h4>", unsafe_allow_html=True)
274
-
275
- # Crear columnas
276
- col1, col2 = st.columns([1, 2])
277
-
278
- # Columnas de entrada
279
- with col1:
280
- target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
281
- product = st.text_input("¿Qué producto/servicio estás promocionando?", placeholder="Ejemplo: Curso de Inglés")
282
-
283
- # Move "Acción deseada" outside the accordion
284
- desired_action = st.text_input("Acción deseada", placeholder="Ejemplo: Registrarse para una prueba gratuita")
285
-
286
- # Move formula selection here, right after desired action
287
- selected_formula_key = st.selectbox(
288
- "Selecciona una fórmula para tus emails",
289
- options=list(email_formulas.email_formulas.keys()),
290
- key="formula_selectbox" # Add a unique key
291
- )
292
-
293
- # Only one submit button with a unique key
294
- submit = st.button("Generar Emails", key="generate_emails_button")
295
-
296
- # Crear un único acordeón para fórmula, creatividad y ángulo
297
- with st.expander("Personaliza tus emails"):
298
- # 1. Idea Creativa al principio con altura de 70px
299
- creative_idea = st.text_area("Idea Creativa", placeholder="Ejemplo: Tu curso es como Netflix: ofrece contenido que engancha y soluciones que la gente realmente quiere ver", height=70)
300
-
301
- # 2. Lo demás (cargador de archivos)
302
- uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia",
303
- type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
304
-
305
- file_content = ""
306
- is_image = False
307
- image_parts = None
308
-
309
- if uploaded_file is not None:
310
- # El código para manejar archivos permanece igual
311
- file_type = uploaded_file.name.split('.')[-1].lower()
312
-
313
- # Manejar archivos de texto
314
- if file_type in ['txt', 'pdf', 'docx']:
315
- # El código para manejar archivos de texto permanece igual
316
- # ...
317
- if file_type == 'txt':
318
- try:
319
- file_content = uploaded_file.read().decode('utf-8')
320
- st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
321
- except Exception as e:
322
- st.error(f"Error al leer el archivo TXT: {str(e)}")
323
- file_content = ""
324
-
325
- elif file_type == 'pdf':
326
- try:
327
- import PyPDF2
328
- pdf_reader = PyPDF2.PdfReader(uploaded_file)
329
- file_content = ""
330
- for page in pdf_reader.pages:
331
- file_content += page.extract_text() + "\n"
332
- st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
333
- except Exception as e:
334
- st.error(f"Error al leer el archivo PDF: {str(e)}")
335
- file_content = ""
336
-
337
- elif file_type == 'docx':
338
- try:
339
- import docx
340
- doc = docx.Document(uploaded_file)
341
- file_content = "\n".join([para.text for para in doc.paragraphs])
342
- st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
343
- except Exception as e:
344
- st.error(f"Error al leer el archivo DOCX: {str(e)}")
345
- file_content = ""
346
-
347
- # Manejar archivos de imagen
348
- elif file_type in ['jpg', 'jpeg', 'png']:
349
- try:
350
- from PIL import Image
351
- image = Image.open(uploaded_file)
352
- image_bytes = uploaded_file.getvalue()
353
- image_parts = {
354
- "mime_type": uploaded_file.type,
355
- "data": image_bytes
356
- }
357
- is_image = True
358
- st.image(image, caption="Imagen cargada", use_column_width=True)
359
- except Exception as e:
360
- st.error(f"Error processing image: {str(e)}")
361
- is_image = False
362
-
363
- # 4. Ángulo
364
- angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
365
- selected_angle = st.selectbox(
366
- "Selecciona un ángulo para tus emails",
367
- options=angle_keys,
368
- key="angle_selectbox" # Add a unique key
369
- )
370
-
371
- # 5. Emoción
372
- emotion = st.selectbox(
373
- "¿Qué emoción quieres evocar?",
374
- options=["Curiosidad", "Miedo", "Esperanza", "Entusiasmo", "Confianza", "Urgencia"],
375
- key="emotion_selectbox" # Add a unique key
376
- )
377
-
378
- # 6. Creatividad (slider)
379
- temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
380
-
381
- selected_formula = email_formulas.email_formulas[selected_formula_key] # Updated reference
382
-
383
- # Removed the submit button from here
384
- # Mostrar los emails generados
385
- if submit:
386
- # Check if we have a valid combination of inputs
387
- has_file = 'file_content' in locals() and file_content.strip() != ""
388
- has_product = product.strip() != ""
389
- has_audience = target_audience.strip() != ""
390
- has_emotion = 'emotion' in locals() and emotion.strip() != ""
391
- has_action = 'desired_action' in locals() and desired_action.strip() != ""
392
-
393
- # Valid combinations:
394
- # 1. File + Product (no audience needed)
395
- # 2. File + Audience (no product needed)
396
- # 3. Product + Audience (traditional way)
397
- valid_inputs = (
398
- (has_file and has_product) or
399
- (has_file and has_audience) or
400
- (has_product and has_audience and has_emotion and has_action)
401
- )
402
-
403
- if valid_inputs and selected_formula:
404
- try:
405
- # Use spinner within col2 context
406
- with col2:
407
- with st.spinner("Creando los emails..."):
408
- # Update the function call to include creative_idea
409
- generated_emails = generate_emails(
410
- target_audience,
411
- product,
412
- temperature,
413
- selected_formula,
414
- selected_angle,
415
- file_content if 'file_content' in locals() else "",
416
- image_parts if 'image_parts' in locals() else None,
417
- is_image if 'is_image' in locals() else False,
418
- emotion,
419
- desired_action,
420
- creative_idea # Add the creative idea parameter
421
- )
422
-
423
- # Clean the response text to remove extra spaces
424
- generated_emails = clean_response_text(generated_emails)
425
-
426
- # Display the generated emails in col2 (still within col2 context)
427
- st.markdown(f"""
428
- <div class="results-container">
429
- <h4>Tus emails persuasivos:</h4>
430
- <p>{generated_emails}</p>
431
- </div>
432
- """, unsafe_allow_html=True)
433
-
434
- # Add download button (styling is in styles/main.css)
435
- import datetime
436
-
437
- # Get current timestamp for the filename
438
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
439
-
440
- # Download button
441
- st.download_button(
442
- label="DESCARGAR EMAILS",
443
- data=generated_emails,
444
- file_name=f"emails_persuasivos_{timestamp}.txt",
445
- mime="text/plain"
446
- )
447
- except ValueError as e:
448
- col2.error(f"Error: {str(e)}")
449
- else:
450
- if not selected_formula:
451
- col2.error("Por favor selecciona una fórmula.")
452
- elif not (has_emotion and has_action):
453
- col2.error("Por favor especifica la emoción que quieres evocar y la acción deseada.")
454
- else:
455
- col2.error("Por favor proporciona al menos una de estas combinaciones: archivo + producto, archivo + público objetivo, o producto + público objetivo + emoción + acción deseada.")
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ import streamlit as st
3
+ import os
4
+ import google.generativeai as genai
5
+ import random
6
+ from streamlit import session_state as state
7
+ from formulas import email_formulas # Updated import statement
8
+ from angles import angles
9
+
10
+ # Cargar las variables de entorno
11
+ load_dotenv()
12
+
13
+ # Configurar la API de Google
14
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
15
+
16
+ # Fórmulas con ejemplos y explicaciones
17
+ # email_formulas dictionary has been moved to formulas/email_formulas.py
18
+
19
+ # Cambiar el nombre de la función
20
+ def generate_emails(target_audience, product, temperature, selected_formula, selected_angle, file_content="", image_parts=None, is_image=False, emotion="", desired_action="", creative_idea=""):
21
+ # Crear la configuración del modelo
22
+ generation_config = {
23
+ "temperature": temperature,
24
+ "top_p": 0.65,
25
+ "top_k": 360,
26
+ "max_output_tokens": 8196,
27
+ }
28
+
29
+ model = genai.GenerativeModel(
30
+ model_name="gemini-2.0-flash",
31
+ generation_config=generation_config,
32
+ )
33
+
34
+ # Definir las instrucciones de formato una sola vez
35
+ format_instructions = """
36
+ FORMAT RULES:
37
+ - Each email must have a clear and attractive subject line
38
+ - The email body must be persuasive and emotional
39
+ - Include a clear call to action
40
+ - Add a professional signature
41
+ - Separate each email with a dividing line
42
+ - Do not include greetings like 'Hello [Name]'
43
+ - Make sure that the postscripts (P.D.) are smaller and more discrete than the main body
44
+ """
45
+
46
+ # Incluir las instrucciones del sistema en el prompt principal
47
+ system_prompt = f"""You are a world-class direct response copywriter trained by Gary Halbert, Gary Bencivenga, and David Ogilvy.
48
+
49
+ You have helped many marketers before me persuade their clients through emotional email sequences.
50
+ Your task is to create a 5-email sequence that makes my [buyer persona] feel [emotion] about my [product/service] and convince them to register/take [desired action].
51
+
52
+ {format_instructions}
53
+
54
+ IMPORTANT:
55
+ - Each email must be unique and memorable
56
+ - Avoid clichés and generalities
57
+ - Maintain a persuasive but credible tone
58
+ - Adapt language to the target audience
59
+ - Focus on transformative benefits"""
60
+
61
+ # Iniciar el prompt con las instrucciones del sistema
62
+ email_instruction = f"{system_prompt}\n\n"
63
+
64
+ # Añadir instrucciones para la idea creativa si existe
65
+ if creative_idea:
66
+ email_instruction += f"""
67
+ CREATIVE CONCEPT:
68
+ Use the following creative concept as the central theme for all emails in the sequence:
69
+ "{creative_idea}"
70
+
71
+ CREATIVE CONCEPT INSTRUCTIONS:
72
+ 1. This concept should be the unifying theme across all emails
73
+ 2. Use it as a metaphor or analogy throughout the sequence
74
+ 3. Develop different aspects of this concept in each email
75
+ 4. Make sure the concept naturally connects to the product benefits
76
+ 5. The concept should make the emails more memorable and engaging
77
+ """
78
+
79
+ # Añadir contenido del archivo si existe
80
+ if file_content:
81
+ email_instruction += f"""
82
+ REFERENCE CONTENT:
83
+ Carefully analyze the following content as a reference for generating emails:
84
+ {file_content[:3000]}
85
+
86
+ ANALYSIS INSTRUCTIONS:
87
+ 1. Extract key information about the product or service mentioned
88
+ 2. Identify the tone, style, and language used
89
+ 3. Detect any data about the target audience or customer avatar
90
+ 4. Look for benefits, features, or pain points mentioned
91
+ 5. Use relevant terms, phrases, or concepts from the content
92
+ 6. Maintain consistency with the brand identity or main message
93
+ 7. Adapt the emails to resonate with the provided content
94
+
95
+ IMPORTANT COMBINATIONS:
96
+ """
97
+ # Updated conditions for specific input combinations
98
+ if product and not target_audience:
99
+ email_instruction += f"""- FILE + PRODUCT: You have a reference document and product ({product}). Create emails that highlight this specific product's benefits and features using insights from the document. Extract audience information from the document to better target the emails.
100
+ """
101
+ elif target_audience and not product:
102
+ email_instruction += f"""- FILE + TARGET AUDIENCE: You have a reference document and target audience ({target_audience}). Create emails tailored to this specific audience using language and concepts from the document. Identify products or services from the document that would appeal to this audience.
103
+ """
104
+ elif product and target_audience:
105
+ email_instruction += f"""- PRODUCT + TARGET AUDIENCE: You have both product ({product}) and target audience ({target_audience}). Create emails that connect this specific product with this specific audience, using insights from the document to strengthen the connection.
106
+ """
107
+
108
+ email_instruction += """
109
+ IMPORTANT: Naturally integrate the elements found in the content with the selected formula and angle.
110
+ """
111
+
112
+ # Preparar instrucciones específicas del ángulo una sola vez
113
+ angle_instructions = ""
114
+ if selected_angle != "NINGUNO":
115
+ angle_instructions = f"""
116
+ MAIN ANGLE: {selected_angle}
117
+ SPECIFIC ANGLE INSTRUCTIONS:
118
+ {angles[selected_angle]["instruction"]}
119
+
120
+ IMPORTANT: The angle {selected_angle} must be applied as a "style layer" over the formula structure:
121
+ 1. Keep the base structure of the formula intact
122
+ 2. Apply the tone and style of the {selected_angle} angle
123
+ 3. Ensure each element of the formula reflects the angle
124
+ 4. The angle affects "how" it's said, not "what" is said
125
+
126
+ SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
127
+ """
128
+ for example in angles[selected_angle]["examples"]:
129
+ angle_instructions += f"- {example}\n"
130
+
131
+ # Añadir las instrucciones del ángulo al prompt principal
132
+ email_instruction += angle_instructions
133
+
134
+ # Dentro de la función, actualizar el prompt para incluir emoción y acción deseada
135
+ email_instruction += (
136
+ f"\nYour task is to create 5 persuasive emails for {target_audience} "
137
+ f"that evoke {emotion} and convince them to {desired_action} about {product}. "
138
+ )
139
+
140
+ # Usar la variable angle_instructions para determinar si hay un ángulo seleccionado
141
+ if angle_instructions:
142
+ email_instruction += f"IMPORTANT: Each email MUST follow the {selected_angle} angle clearly and consistently.\n\n"
143
+
144
+ email_instruction += (
145
+ f"Avoid obvious mentions of {product} and focus on generating genuine interest"
146
+ )
147
+
148
+ if angle_instructions:
149
+ email_instruction += f" using the selected angle"
150
+
151
+ email_instruction += ".\n\n"
152
+
153
+ # Agregar ejemplos de la fórmula
154
+ examples_to_use = selected_formula['examples'][:min(5, len(selected_formula['examples']))]
155
+
156
+ email_instruction += "FORMULA EXAMPLES TO STUDY (ONLY FOR STRUCTURE, NOT CONTENT):\n"
157
+ for i, example in enumerate(examples_to_use, 1):
158
+ email_instruction += f"{i}. {example}\n"
159
+
160
+ # Añadir advertencia crítica con emoji para llamar la atención
161
+ email_instruction += "\n⚠️ WARNING: DO NOT COPY THESE EXAMPLES. Create completely original content with different stories, scenarios and language. The examples are ONLY for understanding the structure.\n"
162
+
163
+ # Consolidar todas las instrucciones específicas en un solo bloque
164
+ specific_instructions = [
165
+ "DO NOT copy the examples directly - create completely original content",
166
+ f"Follow only the logical STRUCTURE and SEQUENCE of the formula for {target_audience} and {product}, never the specific content",
167
+ "Use different vocabulary, scenarios, and narrative approaches than those in the examples",
168
+ "Maintain the same persuasive techniques and psychological triggers but with entirely new content",
169
+ "Create a cohesive sequence where emails reference each other naturally",
170
+ "If an email references a previous one (like 'As I mentioned yesterday'), ensure this is logically consistent",
171
+ "Be creative with stories, analogies, and examples while maintaining the formula's effectiveness"
172
+ ]
173
+
174
+ email_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
175
+ for i, instruction in enumerate(specific_instructions, 1):
176
+ email_instruction += f"{i}. {instruction}\n"
177
+
178
+ # Añadir instrucciones específicas sobre la temperatura creativa
179
+ email_instruction += f"\nCREATIVITY LEVEL: {temperature}. Higher values mean more creative and original content.\n\n"
180
+
181
+ email_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
182
+
183
+ # Consolidar las instrucciones finales en un solo bloque
184
+ final_instructions = [
185
+ "Follow the logical structure but not the exact wording",
186
+ "Be creative and original with your content",
187
+ "Maintain the sequence flow between emails"
188
+ ]
189
+
190
+ # Añadir instrucciones específicas para el ángulo si es necesario
191
+ if selected_angle != "NINGUNO":
192
+ final_instructions.extend([
193
+ "Apply the angle as a 'style layer'",
194
+ "Maintain coherence between formula and angle",
195
+ "Ensure each email reflects both elements"
196
+ ])
197
+
198
+ email_instruction += "\nFINAL REMINDER:\n"
199
+ for i, instruction in enumerate(final_instructions, 1):
200
+ email_instruction += f"{i}. {instruction}\n"
201
+
202
+ # Simplify the final instruction to clearly specify 5 emails
203
+ email_instruction += f"\nGENERATE NOW:\nCreate 5 CREATIVE emails that follow the logical structure but with original content.\n"
204
+
205
+ # Modificar la forma de enviar el mensaje según si hay imagen o no
206
+ message_parts = [email_instruction]
207
+
208
+ # Add the image to the message parts if it exists
209
+ if is_image and image_parts:
210
+ message_parts.append(image_parts)
211
+ instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown, drawing inspiration from the provided image."
212
+ else:
213
+ instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown."
214
+
215
+ # Simplificar las instrucciones finales para evitar redundancia
216
+ instruction_text += " Do not include explanations, only the emails. IMPORTANT: Do not include greetings like 'Hello [Name]' and make sure that the postscripts (P.D.) are smaller and more discrete than the main body of the email, using a lighter format."
217
+
218
+ # Create the chat session with the message parts
219
+ chat_session = model.start_chat(
220
+ history=[
221
+ {
222
+ "role": "user",
223
+ "parts": message_parts,
224
+ },
225
+ ]
226
+ )
227
+
228
+ # Enviar el mensaje con las instrucciones
229
+ response = chat_session.send_message(instruction_text)
230
+
231
+ return response.text
232
+
233
+ # Define the clean_response_text function before it's used
234
+ def clean_response_text(text):
235
+ """Remove extra spaces and normalize whitespace in the response text"""
236
+ import re
237
+ # Replace multiple newlines with just two
238
+ text = re.sub(r'\n{3,}', '\n\n', text)
239
+ # Remove leading/trailing whitespace
240
+ text = text.strip()
241
+ return text
242
+
243
+ # Configurar la interfaz de usuario con Streamlit
244
+ st.set_page_config(
245
+ page_title="Email Composer",
246
+ layout="wide",
247
+ menu_items={}, # Empty dict removes the three-dot menu
248
+ initial_sidebar_state="expanded"
249
+ )
250
+
251
+ # Add custom CSS to reduce margins
252
+ st.markdown("""
253
+ <style>
254
+ .main > div {
255
+ padding-top: 1rem;
256
+ }
257
+ </style>
258
+ """, unsafe_allow_html=True)
259
+
260
+ # Leer el contenido del archivo manual.md
261
+ with open("manual.md", "r", encoding="utf-8") as file:
262
+ manual_content = file.read()
263
+
264
+ # Mostrar el contenido del manual en el sidebar
265
+ st.sidebar.markdown(manual_content)
266
+
267
+ # Load CSS from file
268
+ with open("styles/main.css", "r") as f:
269
+ css = f.read()
270
+
271
+ # Apply the CSS
272
+ st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
273
+
274
+ # Centrar el título y el subtítulo
275
+ st.markdown("<h1 style='text-align: center;'>Generador de Emails</h1>", unsafe_allow_html=True)
276
+ st.markdown("<h4 style='text-align: center;'>Transforma tu marketing con emails persuasivos que convierten. Esta aplicación es tu arma secreta para crear emails emocionales de respuesta directa que impulsan a la acción.</h4>", unsafe_allow_html=True)
277
+
278
+ # Crear columnas
279
+ col1, col2 = st.columns([1, 2])
280
+
281
+ # Columnas de entrada
282
+ with col1:
283
+ target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
284
+ product = st.text_input("¿Qué producto/servicio estás promocionando?", placeholder="Ejemplo: Curso de Inglés")
285
+
286
+ # Move "Acción deseada" outside the accordion
287
+ desired_action = st.text_input("Acción deseada", placeholder="Ejemplo: Registrarse para una prueba gratuita")
288
+
289
+ # Move formula selection here, right after desired action
290
+ selected_formula_key = st.selectbox(
291
+ "Selecciona una fórmula para tus emails",
292
+ options=list(email_formulas.email_formulas.keys()),
293
+ key="formula_selectbox" # Add a unique key
294
+ )
295
+
296
+ # Only one submit button with a unique key
297
+ submit = st.button("Generar Emails", key="generate_emails_button")
298
+
299
+ # Crear un único acordeón para fórmula, creatividad y ángulo
300
+ with st.expander("Personaliza tus emails"):
301
+ # 1. Idea Creativa al principio con altura de 70px
302
+ creative_idea = st.text_area("Idea Creativa", placeholder="Ejemplo: Tu curso es como Netflix: ofrece contenido que engancha y soluciones que la gente realmente quiere ver", height=70)
303
+
304
+ # 2. Lo demás (cargador de archivos)
305
+ uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia",
306
+ type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
307
+
308
+ file_content = ""
309
+ is_image = False
310
+ image_parts = None
311
+
312
+ if uploaded_file is not None:
313
+ # El código para manejar archivos permanece igual
314
+ file_type = uploaded_file.name.split('.')[-1].lower()
315
+
316
+ # Manejar archivos de texto
317
+ if file_type in ['txt', 'pdf', 'docx']:
318
+ # El código para manejar archivos de texto permanece igual
319
+ # ...
320
+ if file_type == 'txt':
321
+ try:
322
+ file_content = uploaded_file.read().decode('utf-8')
323
+ st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
324
+ except Exception as e:
325
+ st.error(f"Error al leer el archivo TXT: {str(e)}")
326
+ file_content = ""
327
+
328
+ elif file_type == 'pdf':
329
+ try:
330
+ import PyPDF2
331
+ pdf_reader = PyPDF2.PdfReader(uploaded_file)
332
+ file_content = ""
333
+ for page in pdf_reader.pages:
334
+ file_content += page.extract_text() + "\n"
335
+ st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
336
+ except Exception as e:
337
+ st.error(f"Error al leer el archivo PDF: {str(e)}")
338
+ file_content = ""
339
+
340
+ elif file_type == 'docx':
341
+ try:
342
+ import docx
343
+ doc = docx.Document(uploaded_file)
344
+ file_content = "\n".join([para.text for para in doc.paragraphs])
345
+ st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
346
+ except Exception as e:
347
+ st.error(f"Error al leer el archivo DOCX: {str(e)}")
348
+ file_content = ""
349
+
350
+ # Manejar archivos de imagen
351
+ elif file_type in ['jpg', 'jpeg', 'png']:
352
+ try:
353
+ from PIL import Image
354
+ image = Image.open(uploaded_file)
355
+ image_bytes = uploaded_file.getvalue()
356
+ image_parts = {
357
+ "mime_type": uploaded_file.type,
358
+ "data": image_bytes
359
+ }
360
+ is_image = True
361
+ st.image(image, caption="Imagen cargada", use_column_width=True)
362
+ except Exception as e:
363
+ st.error(f"Error processing image: {str(e)}")
364
+ is_image = False
365
+
366
+ # 4. Ángulo
367
+ angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
368
+ selected_angle = st.selectbox(
369
+ "Selecciona un ángulo para tus emails",
370
+ options=angle_keys,
371
+ key="angle_selectbox" # Add a unique key
372
+ )
373
+
374
+ # 5. Emoción
375
+ emotion = st.selectbox(
376
+ "¿Qué emoción quieres evocar?",
377
+ options=["Curiosidad", "Miedo", "Esperanza", "Entusiasmo", "Confianza", "Urgencia"],
378
+ key="emotion_selectbox" # Add a unique key
379
+ )
380
+
381
+ # 6. Creatividad (slider)
382
+ temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
383
+
384
+ selected_formula = email_formulas.email_formulas[selected_formula_key] # Updated reference
385
+
386
+ # Removed the submit button from here
387
+ # Mostrar los emails generados
388
+ if submit:
389
+ # Check if we have a valid combination of inputs
390
+ has_file = 'file_content' in locals() and file_content.strip() != ""
391
+ has_product = product.strip() != ""
392
+ has_audience = target_audience.strip() != ""
393
+ has_emotion = 'emotion' in locals() and emotion.strip() != ""
394
+ has_action = 'desired_action' in locals() and desired_action.strip() != ""
395
+
396
+ # Valid combinations:
397
+ # 1. File + Product (no audience needed)
398
+ # 2. File + Audience (no product needed)
399
+ # 3. Product + Audience (traditional way)
400
+ valid_inputs = (
401
+ (has_file and has_product) or
402
+ (has_file and has_audience) or
403
+ (has_product and has_audience and has_emotion and has_action)
404
+ )
405
+
406
+ if valid_inputs and selected_formula:
407
+ try:
408
+ # Use spinner within col2 context
409
+ with col2:
410
+ with st.spinner("Creando los emails..."):
411
+ # Update the function call to include creative_idea
412
+ generated_emails = generate_emails(
413
+ target_audience,
414
+ product,
415
+ temperature,
416
+ selected_formula,
417
+ selected_angle,
418
+ file_content if 'file_content' in locals() else "",
419
+ image_parts if 'image_parts' in locals() else None,
420
+ is_image if 'is_image' in locals() else False,
421
+ emotion,
422
+ desired_action,
423
+ creative_idea # Add the creative idea parameter
424
+ )
425
+
426
+ # Clean the response text to remove extra spaces
427
+ generated_emails = clean_response_text(generated_emails)
428
+
429
+ # Display the generated emails in col2 (still within col2 context)
430
+ st.markdown(f"""
431
+ <div class="results-container">
432
+ <h4>Tus emails persuasivos:</h4>
433
+ <p>{generated_emails}</p>
434
+ </div>
435
+ """, unsafe_allow_html=True)
436
+
437
+ # Add download button (styling is in styles/main.css)
438
+ import datetime
439
+
440
+ # Get current timestamp for the filename
441
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
442
+
443
+ # Download button
444
+ st.download_button(
445
+ label="DESCARGAR EMAILS",
446
+ data=generated_emails,
447
+ file_name=f"emails_persuasivos_{timestamp}.txt",
448
+ mime="text/plain"
449
+ )
450
+ except ValueError as e:
451
+ col2.error(f"Error: {str(e)}")
452
+ else:
453
+ if not selected_formula:
454
+ col2.error("Por favor selecciona una fórmula.")
455
+ elif not (has_emotion and has_action):
456
+ col2.error("Por favor especifica la emoción que quieres evocar y la acción deseada.")
457
+ else:
458
+ col2.error("Por favor proporciona al menos una de estas combinaciones: archivo + producto, archivo + público objetivo, o producto + público objetivo + emoción + acción deseada.")