Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| from pyairtable import Table | |
| import PyPDF2 | |
| import docx | |
| import resend | |
| # Airtable setup | |
| AIRTABLE_API_KEY = os.getenv("AIRTABLE_API_KEY") | |
| BASE_ID = os.getenv("AIRTABLE_BASE_ID") | |
| TABLE_NAME = "user_data" | |
| # Function to safely initialize Airtable | |
| def init_airtable(): | |
| if not all([AIRTABLE_API_KEY, BASE_ID, TABLE_NAME]): | |
| st.sidebar.error("β Airtable configuration is incomplete. Please check your environment variables.") | |
| return None | |
| try: | |
| return Table(AIRTABLE_API_KEY, BASE_ID, TABLE_NAME) | |
| except Exception as e: | |
| st.sidebar.error(f"β Failed to initialize Airtable: {str(e)}") | |
| return None | |
| table = init_airtable() | |
| # Streamlit page setup | |
| st.set_page_config(page_title="Email Genie", page_icon="π§", layout="wide") | |
| # Function to parse CV | |
| def parse_cv(file): | |
| content = "" | |
| if file.name.endswith('.pdf'): | |
| pdf_reader = PyPDF2.PdfReader(file) | |
| for page in pdf_reader.pages: | |
| content += page.extract_text() | |
| elif file.name.endswith('.docx'): | |
| doc = docx.Document(file) | |
| for para in doc.paragraphs: | |
| content += para.text + "\n" | |
| else: | |
| st.error("Unsupported file format. Please upload a PDF or DOCX file.") | |
| return None | |
| return content | |
| # Initialize session state | |
| if 'generated_email' not in st.session_state: | |
| st.session_state.generated_email = "" | |
| if 'page' not in st.session_state: | |
| st.session_state.page = "main" | |
| # Sidebar for user information and navigation | |
| with st.sidebar: | |
| st.title("π§ Email Genie") | |
| email = st.text_input("π§ Enter Your Email") | |
| stored_user_data = None | |
| if email and table: | |
| try: | |
| records = table.all(formula=f"{{Email}}='{email}'") | |
| if records: | |
| st.success("User data found in the database!") | |
| stored_user_data = records[0]['fields'] | |
| else: | |
| st.info("User data not found. Please fill out the remaining details.") | |
| except Exception as e: | |
| st.error(f"Error retrieving user data: {str(e)}") | |
| # Use data from the database if available, otherwise allow manual input | |
| name = st.text_input("Name", value=stored_user_data.get("Name", "") if stored_user_data else "") | |
| profession = st.text_input("Profession", value=stored_user_data.get("Profession", "") if stored_user_data else "") | |
| role = st.text_input("Role", value=stored_user_data.get("Role", "") if stored_user_data else "") | |
| if st.button("Save User Data"): | |
| if not all([name, email, profession, role]): | |
| st.warning("Please fill in all fields before saving.") | |
| elif table: | |
| try: | |
| if stored_user_data: | |
| record_id = records[0]['id'] | |
| table.update(record_id, { | |
| "Name": name, | |
| "Email": email, | |
| "Profession": profession, | |
| "Role": role | |
| }) | |
| st.success("User data updated successfully!") | |
| else: | |
| table.create({ | |
| "Name": name, | |
| "Email": email, | |
| "Profession": profession, | |
| "Role": role | |
| }) | |
| st.success("User data saved successfully!") | |
| except Exception as e: | |
| st.error(f"Error saving user data: {str(e)}") | |
| else: | |
| st.error("Unable to save user data due to Airtable configuration issues.") | |
| # Add a sidebar option to navigate between pages | |
| if st.button("Go to Send Email Page" if st.session_state.page == "main" else "Go to Main Page"): | |
| st.session_state.page = "send_email" if st.session_state.page == "main" else "main" | |
| st.rerun() | |
| # Email template generation setup | |
| gemini_api_key = os.getenv("GEMINI_API_KEY") | |
| if not gemini_api_key: | |
| st.error("β Gemini API key not found. Please set it as a secret.") | |
| st.stop() | |
| try: | |
| llm = ChatGoogleGenerativeAI( | |
| model="gemini-pro", | |
| verbose=True, | |
| temperature=0.7, | |
| google_api_key=gemini_api_key | |
| ) | |
| except Exception as e: | |
| st.error(f"β Failed to initialize Gemini model: {str(e)}") | |
| st.stop() | |
| # Set up dynamic email templates as style guides | |
| templates = { | |
| "Job Application": """Dear {Hiring_Manager}, | |
| I am writing to apply for the position of {Job_Title} at {Company_Name}. With my experience in {Profession} and {Skills}, I believe I can make a valuable contribution to your team. | |
| [Insert 2-3 sentences highlighting relevant experience and skills from the CV] | |
| I look forward to the opportunity to discuss my application in more detail. Please find my attached CV for your reference. | |
| Best regards, | |
| {Name}""", | |
| "Sales Pitch": """Dear {Client_Name}, | |
| I hope this message finds you well. I wanted to introduce you to {Product_Name}, a solution designed to {Value_Proposition}. With my expertise in {Profession}, I believe I can help {Client_Company} achieve {Business_Goal}. | |
| [Insert 1-2 sentences relating the product to the client's specific needs based on research or past interactions] | |
| I would love to schedule a meeting to discuss this in more detail. Please let me know a convenient time for you. | |
| Best regards, | |
| {Name}""", | |
| "Job Offer": """Dear {Candidate_Name}, | |
| We are pleased to offer you the position of {Job_Title} at {Company_Name}. After reviewing your qualifications, we are confident that your skills in {Profession} will make you an excellent addition to our team. | |
| [Insert 1-2 sentences highlighting specific skills or experiences from the candidate's CV that impressed the hiring team] | |
| The starting salary for this position is {Salary}. Please review the attached offer letter for further details. | |
| Best regards, | |
| {Hiring_Manager_Name} | |
| {Company_Name}""" | |
| } | |
| # Main Page | |
| if st.session_state.page == "main": | |
| st.header("π§ Email Genie: Customizable Email Generator") | |
| # Display email template options | |
| st.subheader("Select an Email Type") | |
| email_type = st.selectbox("Email Type", list(templates.keys())) | |
| with st.expander("Email Template Preview", expanded=True): | |
| st.code(templates[email_type], language="text") | |
| # CV file upload (optional) | |
| cv_file = st.file_uploader("Upload your CV (Optional, PDF or DOCX)", type=["pdf", "docx"]) | |
| cv_content = None | |
| if cv_file is not None: | |
| cv_content = parse_cv(cv_file) | |
| if cv_content: | |
| st.success("CV uploaded and parsed successfully!") | |
| else: | |
| st.error("Failed to parse the CV. Please check the file format and try again.") | |
| # Merge user data with dynamic details | |
| details = { | |
| "Name": name, | |
| "Profession": profession, | |
| "Role": role, | |
| "email": email | |
| } | |
| # Conditional input fields based on selected email type | |
| st.subheader("Fill in the Details") | |
| if email_type == "Job Application": | |
| details["Hiring_Manager"] = st.text_input("Hiring Manager's Name") | |
| details["Job_Title"] = st.text_input("Job Title") | |
| details["Company_Name"] = st.text_input("Company Name") | |
| details["Skills"] = st.text_input("Skills") | |
| elif email_type == "Sales Pitch": | |
| details["Client_Name"] = st.text_input("Client Name") | |
| details["Product_Name"] = st.text_input("Product Name") | |
| details["Value_Proposition"] = st.text_area("Value Proposition") | |
| details["Client_Company"] = st.text_input("Client's Company Name") | |
| details["Business_Goal"] = st.text_input("Client's Business Goal") | |
| elif email_type == "Job Offer": | |
| details["Candidate_Name"] = st.text_input("Candidate Name") | |
| details["Job_Title"] = st.text_input("Job Title") | |
| details["Company_Name"] = st.text_input("Company Name") | |
| details["Salary"] = st.text_input("Salary") | |
| details["Hiring_Manager_Name"] = st.text_input("Hiring Manager's Name") | |
| # Generate email based on template style and filled-in details using LLM | |
| if st.button("Generate Email"): | |
| missing_fields = [field for field, value in details.items() if not value] | |
| if missing_fields: | |
| st.warning(f"Please fill in the following fields: {', '.join(missing_fields)}") | |
| else: | |
| try: | |
| with st.spinner("π§ Email Genie is crafting your personalized email..."): | |
| # Update the LLM input prompt | |
| email_prompt = f""" | |
| Generate a professional and personalized email for {email_type} following the style and structure of this template: | |
| {templates[email_type]} | |
| Use these details: {details} | |
| {"CV content: " + cv_content if cv_content else "No CV provided."} | |
| Important: | |
| 1. Create a unique and personalized email, don't copy the template exactly. | |
| 2. Maintain a similar tone and structure to the template. | |
| 3. Include relevant information from the provided details. | |
| 4. If CV content is provided, use specific information from it to personalize the email and highlight relevant skills or experiences. | |
| 5. Ensure the email flows naturally and doesn't feel like it's just inserting information from the CV. | |
| """ | |
| # Generate the email using LLM | |
| response = llm.invoke(email_prompt) | |
| # Extract the generated email from the response | |
| if isinstance(response, str): | |
| st.session_state.generated_email = response | |
| elif hasattr(response, 'content'): | |
| st.session_state.generated_email = response.content | |
| else: | |
| raise ValueError("Unexpected response format from LLM") | |
| if st.session_state.generated_email: | |
| st.success("π Email Genie has crafted your personalized email!") | |
| except Exception as e: | |
| st.error(f"An unexpected error occurred: {str(e)}") | |
| st.error("Please try again or contact support if the issue persists.") | |
| # Display email preview | |
| if st.session_state.generated_email: | |
| st.subheader("Generated Email Preview") | |
| st.text_area("Email Content", st.session_state.generated_email, height=300) | |
| # Information about sending email | |
| st.info(""" | |
| To send the email: | |
| 1. Navigate to the 'Send Email' page using the sidebar. | |
| 2. The generated email will be automatically loaded there. | |
| 3. Edit the email if needed. | |
| 4. Enter the recipient's email address and configure the email sending settings. | |
| 5. Click 'Send Email' to send it. | |
| """) | |
| # Send Email Page | |
| elif st.session_state.page == "send_email": | |
| st.title("π§ββοΈ Email Genie: Send Your Crafted Email") | |
| # Text area for editing the email | |
| email_to_send = st.text_area("Edit your email here", value=st.session_state.get("generated_email", ""), height=300) | |
| # Email sending configuration | |
| st.subheader("Email Sending Configuration") | |
| # Resend API key | |
| resend_api_key = st.text_input("Resend API Key", type="password") | |
| # Sender and recipient email | |
| sender_email = st.text_input("Your Email Address (Sender)") | |
| recipient_email = st.text_input("Recipient Email Address") | |
| if st.button("π Send Email"): | |
| if not email_to_send or not recipient_email or not sender_email or not resend_api_key: | |
| st.warning("Please ensure you have filled in all required fields.") | |
| else: | |
| try: | |
| # Initialize Resend client | |
| resend.api_key = resend_api_key | |
| # Send email using Resend | |
| response = resend.Emails.send({ | |
| "from": sender_email, | |
| "to": recipient_email, | |
| "subject": "Email from Email Genie", | |
| "text": email_to_send | |
| }) | |
| if response['id']: | |
| st.success("π Email sent successfully!") | |
| else: | |
| st.error("Failed to send email. Please check your Resend API key and try again.") | |
| except Exception as e: | |
| st.error(f"Failed to send email: {str(e)}") | |
| st.error("Please check your Resend API key and try again.") | |
| # Add some helpful information for users | |
| st.info(""" | |
| Note: | |
| 1. You need a Resend API key to send emails. If you don't have one, you can sign up at https://resend.com | |
| 2. Make sure your sender email is verified in your Resend account. | |
| """) |