JeCabrera commited on
Commit
4d24e57
·
verified ·
1 Parent(s): 821c442

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +466 -462
app.py CHANGED
@@ -1,462 +1,466 @@
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
- # Fixed number of emails to 5
35
- number_of_emails = 5
36
-
37
- # Incluir las instrucciones del sistema en el prompt principal
38
- system_prompt = f"""You are a world-class direct response copywriter trained by Gary Halbert, Gary Bencivenga, and David Ogilvy.
39
-
40
- You have helped many marketers before me persuade their clients through emotional email sequences.
41
- 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].
42
-
43
- FORMAT RULES:
44
- - Each email must have a clear and attractive subject line
45
- - Include personalized greeting
46
- - The email body must be persuasive and emotional
47
- - Include a clear call to action
48
- - Add a professional signature
49
- - Separate each email with a dividing line
50
-
51
- IMPORTANT ANGLE INSTRUCTIONS:
52
- - The selected angle MUST be applied to EACH email
53
- - The angle modifies HOW the message is expressed, not its structure
54
- - Think of the angle as a "tone overlay" on the content
55
- - The formula provides the structure, the angle provides the style
56
- - Both must work together seamlessly
57
-
58
- FORMAT EXAMPLE:
59
- ---
60
- SUBJECT: [Attractive subject line]
61
-
62
- [Email body with persuasive and emotional content]
63
-
64
- [Clear call to action]
65
-
66
- [Signature]
67
- ---
68
-
69
- IMPORTANT:
70
- - Each email must be unique and memorable
71
- - Avoid clichés and generalities
72
- - Maintain a persuasive but credible tone
73
- - Adapt language to the target audience
74
- - Focus on transformative benefits
75
- - Follow the selected angle style while maintaining the structure"""
76
-
77
- # Add creative idea to system prompt if it exists
78
- if creative_idea:
79
- system_prompt += f"""
80
-
81
- CREATIVE CONCEPT:
82
- Use the following creative concept as the central theme for all emails in the sequence:
83
- "{creative_idea}"
84
-
85
- CREATIVE CONCEPT INSTRUCTIONS:
86
- 1. This concept should be the unifying theme across all emails
87
- 2. Use it as a metaphor or analogy throughout the sequence
88
- 3. Develop different aspects of this concept in each email
89
- 4. Make sure the concept naturally connects to the product benefits
90
- 5. The concept should make the emails more memorable and engaging
91
- """
92
-
93
- # Iniciar el prompt con las instrucciones del sistema
94
- email_instruction = f"{system_prompt}\n\n"
95
-
96
- # Añadir contenido del archivo si existe
97
- if file_content:
98
- email_instruction += f"""
99
- REFERENCE CONTENT:
100
- Carefully analyze the following content as a reference for generating emails:
101
- {file_content[:3000]}
102
-
103
- ANALYSIS INSTRUCTIONS:
104
- 1. Extract key information about the product or service mentioned
105
- 2. Identify the tone, style, and language used
106
- 3. Detect any data about the target audience or customer avatar
107
- 4. Look for benefits, features, or pain points mentioned
108
- 5. Use relevant terms, phrases, or concepts from the content
109
- 6. Maintain consistency with the brand identity or main message
110
- 7. Adapt the emails to resonate with the provided content
111
-
112
- IMPORTANT COMBINATIONS:
113
- """
114
- # Updated conditions for specific input combinations
115
- if product and not target_audience:
116
- 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.
117
- """
118
- elif target_audience and not product:
119
- 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.
120
- """
121
- elif product and target_audience:
122
- 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.
123
- """
124
-
125
- email_instruction += """
126
- IMPORTANT: Naturally integrate the elements found in the content with the selected formula and angle.
127
- """
128
-
129
- # Añadir instrucciones de ángulo solo si no es "NINGUNO"
130
- if selected_angle != "NINGUNO":
131
- email_instruction += f"""
132
- MAIN ANGLE: {selected_angle}
133
- SPECIFIC ANGLE INSTRUCTIONS:
134
- {angles[selected_angle]["instruction"]}
135
-
136
- IMPORTANT: The angle {selected_angle} must be applied as a "style layer" over the formula structure:
137
- 1. Keep the base structure of the formula intact
138
- 2. Apply the tone and style of the {selected_angle} angle
139
- 3. Ensure each element of the formula reflects the angle
140
- 4. The angle affects "how" it's said, not "what" is said
141
-
142
- SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
143
- """
144
- for example in angles[selected_angle]["examples"]:
145
- email_instruction += f"- {example}\n"
146
-
147
- # Dentro de la función, actualizar el prompt para incluir emoción y acción deseada
148
- email_instruction += (
149
- f"\nYour task is to create {number_of_emails} persuasive emails for {target_audience} "
150
- f"that evoke {emotion} and convince them to {desired_action} about {product}. "
151
- )
152
-
153
- if selected_angle != "NINGUNO":
154
- email_instruction += f"IMPORTANT: Each email MUST follow the {selected_angle} angle clearly and consistently.\n\n"
155
-
156
- email_instruction += (
157
- f"Avoid obvious mentions of {product} and focus on generating genuine interest"
158
- )
159
-
160
- if selected_angle != "NINGUNO":
161
- email_instruction += f" using the selected angle"
162
-
163
- email_instruction += ".\n\n"
164
-
165
- email_instruction += (
166
- f"IMPORTANT: Carefully study these examples of the selected formula. "
167
- f"Each example represents the style and structure to follow"
168
- )
169
-
170
- if selected_angle != "NINGUNO":
171
- email_instruction += f", adapted to the {selected_angle} angle"
172
-
173
- email_instruction += ":\n\n"
174
-
175
- # Agregar 5 ejemplos aleatorios de la fórmula
176
- random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
177
-
178
- email_instruction += "FORMULA EXAMPLES TO FOLLOW:\n"
179
- for i, example in enumerate(random_examples, 1):
180
- email_instruction += f"{i}. {example}\n"
181
-
182
- email_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
183
- email_instruction += "1. Maintain the same structure and length as the previous examples\n"
184
- email_instruction += "2. Use the same tone and writing style\n"
185
- email_instruction += "3. Replicate the phrase construction patterns\n"
186
- email_instruction += "4. Preserve the level of specificity and detail\n"
187
- email_instruction += f"5. Adapt the content for {target_audience} while maintaining the essence of the examples\n\n"
188
-
189
- email_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
190
-
191
- # CORRECTO (con indentación):
192
- if selected_angle != "NINGUNO":
193
- email_instruction += f"""
194
- FINAL REMINDER:
195
- 1. Follow the structure of the selected formula
196
- 2. Apply the angle as a "style layer"
197
- 3. Maintain coherence between formula and angle
198
- 4. Ensure each email reflects both elements
199
-
200
- GENERATE NOW:
201
- Create {number_of_emails} emails that faithfully follow the style and structure of the examples shown.
202
- """
203
- else:
204
- email_instruction += f"""
205
- GENERATE NOW:
206
- Create {number_of_emails} emails that faithfully follow the style and structure of the examples shown.
207
- """
208
-
209
- # Modificar la forma de enviar el mensaje según si hay imagen o no
210
- message_parts = [email_instruction]
211
-
212
- # Add the image to the message parts if it exists
213
- if is_image and image_parts:
214
- message_parts.append(image_parts)
215
- instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown, drawing inspiration from the provided image."
216
- else:
217
- instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown."
218
-
219
- # Common instruction for both cases
220
- 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."
221
-
222
- # Create the chat session with the message parts
223
- chat_session = model.start_chat(
224
- history=[
225
- {
226
- "role": "user",
227
- "parts": message_parts,
228
- },
229
- ]
230
- )
231
-
232
- # Enviar el mensaje con las instrucciones
233
- response = chat_session.send_message(instruction_text)
234
-
235
- return response.text
236
-
237
- # Define the clean_response_text function before it's used
238
- def clean_response_text(text):
239
- """Remove extra spaces and normalize whitespace in the response text"""
240
- import re
241
- # Replace multiple newlines with just two
242
- text = re.sub(r'\n{3,}', '\n\n', text)
243
- # Remove leading/trailing whitespace
244
- text = text.strip()
245
- return text
246
-
247
- # Configurar la interfaz de usuario con Streamlit
248
- st.set_page_config(
249
- page_title="Email Composer",
250
- layout="wide",
251
- menu_items={}, # Empty dict removes the three-dot menu
252
- initial_sidebar_state="expanded"
253
- )
254
-
255
- # Add custom CSS to reduce margins
256
- st.markdown("""
257
- <style>
258
- .main > div {
259
- padding-top: 1rem;
260
- }
261
- </style>
262
- """, unsafe_allow_html=True)
263
-
264
- # Leer el contenido del archivo manual.md
265
- with open("manual.md", "r", encoding="utf-8") as file:
266
- manual_content = file.read()
267
-
268
- # Mostrar el contenido del manual en el sidebar
269
- st.sidebar.markdown(manual_content)
270
-
271
- # Load CSS from file
272
- with open("styles/main.css", "r") as f:
273
- css = f.read()
274
-
275
- # Apply the CSS
276
- st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
277
-
278
- # Centrar el título y el subtítulo
279
- st.markdown("<h1 style='text-align: center;'>Generador de Emails</h1>", unsafe_allow_html=True)
280
- 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)
281
-
282
- # Crear columnas
283
- col1, col2 = st.columns([1, 2])
284
-
285
- # Columnas de entrada
286
- with col1:
287
- target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
288
- product = st.text_input("¿Qué producto/servicio estás promocionando?", placeholder="Ejemplo: Curso de Inglés")
289
-
290
- # Move "Acción deseada" outside the accordion
291
- desired_action = st.text_input("Acción deseada", placeholder="Ejemplo: Registrarse para una prueba gratuita")
292
-
293
- # Move formula selection here, right after desired action
294
- selected_formula_key = st.selectbox(
295
- "Selecciona una fórmula para tus emails",
296
- options=list(email_formulas.email_formulas.keys()),
297
- key="formula_selectbox" # Add a unique key
298
- )
299
-
300
- # Only one submit button with a unique key
301
- submit = st.button("Generar Emails", key="generate_emails_button")
302
-
303
- # Crear un único acordeón para fórmula, creatividad y ángulo
304
- with st.expander("Personaliza tus emails"):
305
- # 1. Idea Creativa al principio con altura de 70px
306
- 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)
307
-
308
- # 2. Lo demás (cargador de archivos)
309
- uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia",
310
- type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
311
-
312
- file_content = ""
313
- is_image = False
314
- image_parts = None
315
-
316
- if uploaded_file is not None:
317
- # El código para manejar archivos permanece igual
318
- file_type = uploaded_file.name.split('.')[-1].lower()
319
-
320
- # Manejar archivos de texto
321
- if file_type in ['txt', 'pdf', 'docx']:
322
- # El código para manejar archivos de texto permanece igual
323
- # ...
324
- if file_type == 'txt':
325
- try:
326
- file_content = uploaded_file.read().decode('utf-8')
327
- st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
328
- except Exception as e:
329
- st.error(f"Error al leer el archivo TXT: {str(e)}")
330
- file_content = ""
331
-
332
- elif file_type == 'pdf':
333
- try:
334
- import PyPDF2
335
- pdf_reader = PyPDF2.PdfReader(uploaded_file)
336
- file_content = ""
337
- for page in pdf_reader.pages:
338
- file_content += page.extract_text() + "\n"
339
- st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
340
- except Exception as e:
341
- st.error(f"Error al leer el archivo PDF: {str(e)}")
342
- file_content = ""
343
-
344
- elif file_type == 'docx':
345
- try:
346
- import docx
347
- doc = docx.Document(uploaded_file)
348
- file_content = "\n".join([para.text for para in doc.paragraphs])
349
- st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
350
- except Exception as e:
351
- st.error(f"Error al leer el archivo DOCX: {str(e)}")
352
- file_content = ""
353
-
354
- # Manejar archivos de imagen
355
- elif file_type in ['jpg', 'jpeg', 'png']:
356
- try:
357
- from PIL import Image
358
- image = Image.open(uploaded_file)
359
- image_bytes = uploaded_file.getvalue()
360
- image_parts = {
361
- "mime_type": uploaded_file.type,
362
- "data": image_bytes
363
- }
364
- is_image = True
365
- st.image(image, caption="Imagen cargada", use_column_width=True)
366
- except Exception as e:
367
- st.error(f"Error processing image: {str(e)}")
368
- is_image = False
369
-
370
- # 4. Ángulo
371
- angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
372
- selected_angle = st.selectbox(
373
- "Selecciona un ángulo para tus emails",
374
- options=angle_keys,
375
- key="angle_selectbox" # Add a unique key
376
- )
377
-
378
- # 5. Emoción
379
- emotion = st.selectbox(
380
- "¿Qué emoción quieres evocar?",
381
- options=["Curiosidad", "Miedo", "Esperanza", "Entusiasmo", "Confianza", "Urgencia"],
382
- key="emotion_selectbox" # Add a unique key
383
- )
384
-
385
- # 6. Creatividad (slider)
386
- temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
387
-
388
- selected_formula = email_formulas.email_formulas[selected_formula_key] # Updated reference
389
-
390
- # Removed the submit button from here
391
- # Mostrar los emails generados
392
- if submit:
393
- # Check if we have a valid combination of inputs
394
- has_file = 'file_content' in locals() and file_content.strip() != ""
395
- has_product = product.strip() != ""
396
- has_audience = target_audience.strip() != ""
397
- has_emotion = 'emotion' in locals() and emotion.strip() != ""
398
- has_action = 'desired_action' in locals() and desired_action.strip() != ""
399
-
400
- # Valid combinations:
401
- # 1. File + Product (no audience needed)
402
- # 2. File + Audience (no product needed)
403
- # 3. Product + Audience (traditional way)
404
- valid_inputs = (
405
- (has_file and has_product) or
406
- (has_file and has_audience) or
407
- (has_product and has_audience and has_emotion and has_action)
408
- )
409
-
410
- if valid_inputs and selected_formula:
411
- try:
412
- # Use spinner within col2 context
413
- with col2:
414
- with st.spinner("Creando los emails..."):
415
- # Update the function call to include creative_idea
416
- generated_emails = generate_emails(
417
- target_audience,
418
- product,
419
- temperature,
420
- selected_formula,
421
- selected_angle,
422
- file_content if 'file_content' in locals() else "",
423
- image_parts if 'image_parts' in locals() else None,
424
- is_image if 'is_image' in locals() else False,
425
- emotion,
426
- desired_action,
427
- creative_idea # Add the creative idea parameter
428
- )
429
-
430
- # Clean the response text to remove extra spaces
431
- generated_emails = clean_response_text(generated_emails)
432
-
433
- # Display the generated emails in col2 (still within col2 context)
434
- st.markdown(f"""
435
- <div class="results-container">
436
- <h4>Tus emails persuasivos:</h4>
437
- <p>{generated_emails}</p>
438
- </div>
439
- """, unsafe_allow_html=True)
440
-
441
- # Add download button (styling is in styles/main.css)
442
- import datetime
443
-
444
- # Get current timestamp for the filename
445
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
446
-
447
- # Download button
448
- st.download_button(
449
- label="DESCARGAR EMAILS",
450
- data=generated_emails,
451
- file_name=f"emails_persuasivos_{timestamp}.txt",
452
- mime="text/plain"
453
- )
454
- except ValueError as e:
455
- col2.error(f"Error: {str(e)}")
456
- else:
457
- if not selected_formula:
458
- col2.error("Por favor selecciona una fórmula.")
459
- elif not (has_emotion and has_action):
460
- col2.error("Por favor especifica la emoción que quieres evocar y la acción deseada.")
461
- else:
462
- 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
+ # Fixed number of emails to 5
35
+ number_of_emails = 5
36
+
37
+ # Incluir las instrucciones del sistema en el prompt principal
38
+ system_prompt = f"""You are a world-class direct response copywriter trained by Gary Halbert, Gary Bencivenga, and David Ogilvy.
39
+
40
+ You have helped many marketers before me persuade their clients through emotional email sequences.
41
+ 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].
42
+
43
+ FORMAT RULES:
44
+ - Each email must have a clear and attractive subject line
45
+ - Include personalized greeting
46
+ - The email body must be persuasive and emotional
47
+ - Include a clear call to action
48
+ - Add a professional signature
49
+ - Separate each email with a dividing line
50
+
51
+ IMPORTANT ANGLE INSTRUCTIONS:
52
+ - The selected angle MUST be applied to EACH email
53
+ - The angle modifies HOW the message is expressed, not its structure
54
+ - Think of the angle as a "tone overlay" on the content
55
+ - The formula provides the structure, the angle provides the style
56
+ - Both must work together seamlessly
57
+
58
+ FORMAT EXAMPLE:
59
+ ---
60
+ SUBJECT: [Attractive subject line]
61
+
62
+ [Email body with persuasive and emotional content]
63
+
64
+ [Clear call to action]
65
+
66
+ [Signature]
67
+ ---
68
+
69
+ IMPORTANT:
70
+ - Each email must be unique and memorable
71
+ - Avoid clichés and generalities
72
+ - Maintain a persuasive but credible tone
73
+ - Adapt language to the target audience
74
+ - Focus on transformative benefits
75
+ - Follow the selected angle style while maintaining the structure"""
76
+
77
+ # Add creative idea to system prompt if it exists
78
+ if creative_idea:
79
+ system_prompt += f"""
80
+
81
+ CREATIVE CONCEPT:
82
+ Use the following creative concept as the central theme for all emails in the sequence:
83
+ "{creative_idea}"
84
+
85
+ CREATIVE CONCEPT INSTRUCTIONS:
86
+ 1. This concept should be the unifying theme across all emails
87
+ 2. Use it as a metaphor or analogy throughout the sequence
88
+ 3. Develop different aspects of this concept in each email
89
+ 4. Make sure the concept naturally connects to the product benefits
90
+ 5. The concept should make the emails more memorable and engaging
91
+ """
92
+
93
+ # Iniciar el prompt con las instrucciones del sistema
94
+ email_instruction = f"{system_prompt}\n\n"
95
+
96
+ # Añadir contenido del archivo si existe
97
+ if file_content:
98
+ email_instruction += f"""
99
+ REFERENCE CONTENT:
100
+ Carefully analyze the following content as a reference for generating emails:
101
+ {file_content[:3000]}
102
+
103
+ ANALYSIS INSTRUCTIONS:
104
+ 1. Extract key information about the product or service mentioned
105
+ 2. Identify the tone, style, and language used
106
+ 3. Detect any data about the target audience or customer avatar
107
+ 4. Look for benefits, features, or pain points mentioned
108
+ 5. Use relevant terms, phrases, or concepts from the content
109
+ 6. Maintain consistency with the brand identity or main message
110
+ 7. Adapt the emails to resonate with the provided content
111
+
112
+ IMPORTANT COMBINATIONS:
113
+ """
114
+ # Updated conditions for specific input combinations
115
+ if product and not target_audience:
116
+ 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.
117
+ """
118
+ elif target_audience and not product:
119
+ 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.
120
+ """
121
+ elif product and target_audience:
122
+ 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.
123
+ """
124
+
125
+ email_instruction += """
126
+ IMPORTANT: Naturally integrate the elements found in the content with the selected formula and angle.
127
+ """
128
+
129
+ # Añadir instrucciones de ángulo solo si no es "NINGUNO"
130
+ if selected_angle != "NINGUNO":
131
+ email_instruction += f"""
132
+ MAIN ANGLE: {selected_angle}
133
+ SPECIFIC ANGLE INSTRUCTIONS:
134
+ {angles[selected_angle]["instruction"]}
135
+
136
+ IMPORTANT: The angle {selected_angle} must be applied as a "style layer" over the formula structure:
137
+ 1. Keep the base structure of the formula intact
138
+ 2. Apply the tone and style of the {selected_angle} angle
139
+ 3. Ensure each element of the formula reflects the angle
140
+ 4. The angle affects "how" it's said, not "what" is said
141
+
142
+ SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
143
+ """
144
+ for example in angles[selected_angle]["examples"]:
145
+ email_instruction += f"- {example}\n"
146
+
147
+ # Dentro de la función, actualizar el prompt para incluir emoción y acción deseada
148
+ email_instruction += (
149
+ f"\nYour task is to create {number_of_emails} persuasive emails for {target_audience} "
150
+ f"that evoke {emotion} and convince them to {desired_action} about {product}. "
151
+ )
152
+
153
+ if selected_angle != "NINGUNO":
154
+ email_instruction += f"IMPORTANT: Each email MUST follow the {selected_angle} angle clearly and consistently.\n\n"
155
+
156
+ email_instruction += (
157
+ f"Avoid obvious mentions of {product} and focus on generating genuine interest"
158
+ )
159
+
160
+ if selected_angle != "NINGUNO":
161
+ email_instruction += f" using the selected angle"
162
+
163
+ email_instruction += ".\n\n"
164
+
165
+ email_instruction += (
166
+ f"IMPORTANT: Carefully study these examples of the selected formula. "
167
+ f"Each example represents the style and structure to follow"
168
+ )
169
+
170
+ if selected_angle != "NINGUNO":
171
+ email_instruction += f", adapted to the {selected_angle} angle"
172
+
173
+ email_instruction += ":\n\n"
174
+
175
+ # En lugar de seleccionar ejemplos aleatorios, tomar los primeros 5 (o todos si hay menos)
176
+ examples_to_use = selected_formula['examples'][:min(5, len(selected_formula['examples']))]
177
+
178
+ email_instruction += "FORMULA EXAMPLES TO FOLLOW:\n"
179
+ for i, example in enumerate(examples_to_use, 1):
180
+ email_instruction += f"{i}. {example}\n"
181
+
182
+ email_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
183
+ email_instruction += "1. Maintain the same structure and length as the previous examples\n"
184
+ email_instruction += "2. Use the same tone and writing style\n"
185
+ email_instruction += "3. Replicate the phrase construction patterns\n"
186
+ email_instruction += "4. Preserve the level of specificity and detail\n"
187
+ email_instruction += f"5. Adapt the content for {target_audience} while maintaining the essence of the examples\n"
188
+ email_instruction += "6. IMPORTANT: Each of your 5 emails should directly follow the structure and logic of the corresponding example\n"
189
+ email_instruction += " - Email 1 should follow Example 1's structure and logic\n"
190
+ email_instruction += " - Email 2 should follow Example 2's structure and logic\n"
191
+ email_instruction += " - And so on for all 5 emails\n\n"
192
+
193
+ email_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
194
+
195
+ # CORRECTO (con indentación):
196
+ if selected_angle != "NINGUNO":
197
+ email_instruction += f"""
198
+ FINAL REMINDER:
199
+ 1. Follow the structure of the selected formula
200
+ 2. Apply the angle as a "style layer"
201
+ 3. Maintain coherence between formula and angle
202
+ 4. Ensure each email reflects both elements
203
+
204
+ GENERATE NOW:
205
+ Create {number_of_emails} emails that faithfully follow the style and structure of the examples shown.
206
+ """
207
+ else:
208
+ email_instruction += f"""
209
+ GENERATE NOW:
210
+ Create {number_of_emails} emails that faithfully follow the style and structure of the examples shown.
211
+ """
212
+
213
+ # Modificar la forma de enviar el mensaje según si hay imagen o no
214
+ message_parts = [email_instruction]
215
+
216
+ # Add the image to the message parts if it exists
217
+ if is_image and image_parts:
218
+ message_parts.append(image_parts)
219
+ instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown, drawing inspiration from the provided image."
220
+ else:
221
+ instruction_text = "Generate the emails in Spanish following exactly the style of the examples shown."
222
+
223
+ # Common instruction for both cases
224
+ 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."
225
+
226
+ # Create the chat session with the message parts
227
+ chat_session = model.start_chat(
228
+ history=[
229
+ {
230
+ "role": "user",
231
+ "parts": message_parts,
232
+ },
233
+ ]
234
+ )
235
+
236
+ # Enviar el mensaje con las instrucciones
237
+ response = chat_session.send_message(instruction_text)
238
+
239
+ return response.text
240
+
241
+ # Define the clean_response_text function before it's used
242
+ def clean_response_text(text):
243
+ """Remove extra spaces and normalize whitespace in the response text"""
244
+ import re
245
+ # Replace multiple newlines with just two
246
+ text = re.sub(r'\n{3,}', '\n\n', text)
247
+ # Remove leading/trailing whitespace
248
+ text = text.strip()
249
+ return text
250
+
251
+ # Configurar la interfaz de usuario con Streamlit
252
+ st.set_page_config(
253
+ page_title="Email Composer",
254
+ layout="wide",
255
+ menu_items={}, # Empty dict removes the three-dot menu
256
+ initial_sidebar_state="expanded"
257
+ )
258
+
259
+ # Add custom CSS to reduce margins
260
+ st.markdown("""
261
+ <style>
262
+ .main > div {
263
+ padding-top: 1rem;
264
+ }
265
+ </style>
266
+ """, unsafe_allow_html=True)
267
+
268
+ # Leer el contenido del archivo manual.md
269
+ with open("manual.md", "r", encoding="utf-8") as file:
270
+ manual_content = file.read()
271
+
272
+ # Mostrar el contenido del manual en el sidebar
273
+ st.sidebar.markdown(manual_content)
274
+
275
+ # Load CSS from file
276
+ with open("styles/main.css", "r") as f:
277
+ css = f.read()
278
+
279
+ # Apply the CSS
280
+ st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
281
+
282
+ # Centrar el título y el subtítulo
283
+ st.markdown("<h1 style='text-align: center;'>Generador de Emails</h1>", unsafe_allow_html=True)
284
+ 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)
285
+
286
+ # Crear columnas
287
+ col1, col2 = st.columns([1, 2])
288
+
289
+ # Columnas de entrada
290
+ with col1:
291
+ target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
292
+ product = st.text_input("¿Qué producto/servicio estás promocionando?", placeholder="Ejemplo: Curso de Inglés")
293
+
294
+ # Move "Acción deseada" outside the accordion
295
+ desired_action = st.text_input("Acción deseada", placeholder="Ejemplo: Registrarse para una prueba gratuita")
296
+
297
+ # Move formula selection here, right after desired action
298
+ selected_formula_key = st.selectbox(
299
+ "Selecciona una fórmula para tus emails",
300
+ options=list(email_formulas.email_formulas.keys()),
301
+ key="formula_selectbox" # Add a unique key
302
+ )
303
+
304
+ # Only one submit button with a unique key
305
+ submit = st.button("Generar Emails", key="generate_emails_button")
306
+
307
+ # Crear un único acordeón para fórmula, creatividad y ángulo
308
+ with st.expander("Personaliza tus emails"):
309
+ # 1. Idea Creativa al principio con altura de 70px
310
+ 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)
311
+
312
+ # 2. Lo demás (cargador de archivos)
313
+ uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia",
314
+ type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
315
+
316
+ file_content = ""
317
+ is_image = False
318
+ image_parts = None
319
+
320
+ if uploaded_file is not None:
321
+ # El código para manejar archivos permanece igual
322
+ file_type = uploaded_file.name.split('.')[-1].lower()
323
+
324
+ # Manejar archivos de texto
325
+ if file_type in ['txt', 'pdf', 'docx']:
326
+ # El código para manejar archivos de texto permanece igual
327
+ # ...
328
+ if file_type == 'txt':
329
+ try:
330
+ file_content = uploaded_file.read().decode('utf-8')
331
+ st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
332
+ except Exception as e:
333
+ st.error(f"Error al leer el archivo TXT: {str(e)}")
334
+ file_content = ""
335
+
336
+ elif file_type == 'pdf':
337
+ try:
338
+ import PyPDF2
339
+ pdf_reader = PyPDF2.PdfReader(uploaded_file)
340
+ file_content = ""
341
+ for page in pdf_reader.pages:
342
+ file_content += page.extract_text() + "\n"
343
+ st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
344
+ except Exception as e:
345
+ st.error(f"Error al leer el archivo PDF: {str(e)}")
346
+ file_content = ""
347
+
348
+ elif file_type == 'docx':
349
+ try:
350
+ import docx
351
+ doc = docx.Document(uploaded_file)
352
+ file_content = "\n".join([para.text for para in doc.paragraphs])
353
+ st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
354
+ except Exception as e:
355
+ st.error(f"Error al leer el archivo DOCX: {str(e)}")
356
+ file_content = ""
357
+
358
+ # Manejar archivos de imagen
359
+ elif file_type in ['jpg', 'jpeg', 'png']:
360
+ try:
361
+ from PIL import Image
362
+ image = Image.open(uploaded_file)
363
+ image_bytes = uploaded_file.getvalue()
364
+ image_parts = {
365
+ "mime_type": uploaded_file.type,
366
+ "data": image_bytes
367
+ }
368
+ is_image = True
369
+ st.image(image, caption="Imagen cargada", use_column_width=True)
370
+ except Exception as e:
371
+ st.error(f"Error processing image: {str(e)}")
372
+ is_image = False
373
+
374
+ # 4. Ángulo
375
+ angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
376
+ selected_angle = st.selectbox(
377
+ "Selecciona un ángulo para tus emails",
378
+ options=angle_keys,
379
+ key="angle_selectbox" # Add a unique key
380
+ )
381
+
382
+ # 5. Emoción
383
+ emotion = st.selectbox(
384
+ "¿Qué emoción quieres evocar?",
385
+ options=["Curiosidad", "Miedo", "Esperanza", "Entusiasmo", "Confianza", "Urgencia"],
386
+ key="emotion_selectbox" # Add a unique key
387
+ )
388
+
389
+ # 6. Creatividad (slider)
390
+ temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
391
+
392
+ selected_formula = email_formulas.email_formulas[selected_formula_key] # Updated reference
393
+
394
+ # Removed the submit button from here
395
+ # Mostrar los emails generados
396
+ if submit:
397
+ # Check if we have a valid combination of inputs
398
+ has_file = 'file_content' in locals() and file_content.strip() != ""
399
+ has_product = product.strip() != ""
400
+ has_audience = target_audience.strip() != ""
401
+ has_emotion = 'emotion' in locals() and emotion.strip() != ""
402
+ has_action = 'desired_action' in locals() and desired_action.strip() != ""
403
+
404
+ # Valid combinations:
405
+ # 1. File + Product (no audience needed)
406
+ # 2. File + Audience (no product needed)
407
+ # 3. Product + Audience (traditional way)
408
+ valid_inputs = (
409
+ (has_file and has_product) or
410
+ (has_file and has_audience) or
411
+ (has_product and has_audience and has_emotion and has_action)
412
+ )
413
+
414
+ if valid_inputs and selected_formula:
415
+ try:
416
+ # Use spinner within col2 context
417
+ with col2:
418
+ with st.spinner("Creando los emails..."):
419
+ # Update the function call to include creative_idea
420
+ generated_emails = generate_emails(
421
+ target_audience,
422
+ product,
423
+ temperature,
424
+ selected_formula,
425
+ selected_angle,
426
+ file_content if 'file_content' in locals() else "",
427
+ image_parts if 'image_parts' in locals() else None,
428
+ is_image if 'is_image' in locals() else False,
429
+ emotion,
430
+ desired_action,
431
+ creative_idea # Add the creative idea parameter
432
+ )
433
+
434
+ # Clean the response text to remove extra spaces
435
+ generated_emails = clean_response_text(generated_emails)
436
+
437
+ # Display the generated emails in col2 (still within col2 context)
438
+ st.markdown(f"""
439
+ <div class="results-container">
440
+ <h4>Tus emails persuasivos:</h4>
441
+ <p>{generated_emails}</p>
442
+ </div>
443
+ """, unsafe_allow_html=True)
444
+
445
+ # Add download button (styling is in styles/main.css)
446
+ import datetime
447
+
448
+ # Get current timestamp for the filename
449
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
450
+
451
+ # Download button
452
+ st.download_button(
453
+ label="DESCARGAR EMAILS",
454
+ data=generated_emails,
455
+ file_name=f"emails_persuasivos_{timestamp}.txt",
456
+ mime="text/plain"
457
+ )
458
+ except ValueError as e:
459
+ col2.error(f"Error: {str(e)}")
460
+ else:
461
+ if not selected_formula:
462
+ col2.error("Por favor selecciona una fórmula.")
463
+ elif not (has_emotion and has_action):
464
+ col2.error("Por favor especifica la emoción que quieres evocar y la acción deseada.")
465
+ else:
466
+ 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.")