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

Upload 9 files

Browse files
Files changed (2) hide show
  1. app.py +462 -462
  2. styles/main.css +3 -3
app.py CHANGED
@@ -1,462 +1,462 @@
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
+ # 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.")
styles/main.css CHANGED
@@ -72,14 +72,14 @@ div.stButton > button:hover {
72
  font-weight: bold;
73
  border: none;
74
  width: 80%;
75
- margin: 10px auto; /* Add top and bottom margin */
76
  display: block;
77
  }
78
 
79
  /* Results container styling */
80
  .results-container {
81
- margin-bottom: 0px;
82
- padding: 10px;
83
  border-radius: 5px;
84
  background-color: #f8f9fa;
85
  }
 
72
  font-weight: bold;
73
  border: none;
74
  width: 80%;
75
+ margin: 20px auto; /* Add top and bottom margin */
76
  display: block;
77
  }
78
 
79
  /* Results container styling */
80
  .results-container {
81
+ margin-bottom: 20px;
82
+ padding: 15px;
83
  border-radius: 5px;
84
  background-color: #f8f9fa;
85
  }