JeCabrera commited on
Commit
5a831ce
·
verified ·
1 Parent(s): 46d6bde

Upload 9 files

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