Spaces:
Sleeping
Sleeping
| import os | |
| import random | |
| import json | |
| import openai | |
| import streamlit as st | |
| from dotenv import load_dotenv | |
| from st_copy import copy_button | |
| from cryptography.fernet import Fernet | |
| # Load environment variables from .env file | |
| load_dotenv() | |
| # Set page config - MUST BE THE FIRST STREAMLIT COMMAND | |
| st.set_page_config( | |
| page_title="Petition Email Generator", | |
| page_icon="π§", | |
| layout="wide", | |
| ) | |
| # Get the password token from environment variables | |
| PASSWORD_TOKEN = os.environ.get("PASSWORD_TOKEN") | |
| # Function to load encrypted email examples | |
| def load_email_examples(): | |
| try: | |
| # Get the encryption key from environment variables | |
| encryption_key = os.environ.get("ENCRYPTION_KEY") | |
| if not encryption_key: | |
| st.warning("Encryption key not found in environment variables.") | |
| return None | |
| # Simple path to the encrypted file | |
| file_path = "examples/encrypted_examples.bin" | |
| # Check if the file exists | |
| if not os.path.exists(file_path): | |
| st.warning(f"Encrypted examples file not found at {file_path}") | |
| return None | |
| # Read the encrypted file | |
| with open(file_path, "rb") as file: | |
| encrypted_data = file.read() | |
| # Decrypt the data | |
| fernet = Fernet(encryption_key) | |
| decrypted_data = fernet.decrypt(encrypted_data).decode() | |
| # Parse the JSON data | |
| examples_data = json.loads(decrypted_data) | |
| return examples_data | |
| except Exception as e: | |
| st.error(f"Error loading examples: {str(e)}") | |
| return None | |
| # Initialize authentication state | |
| if 'is_authenticated' not in st.session_state: | |
| st.session_state.is_authenticated = False | |
| # If not authenticated, show login form | |
| if not st.session_state.is_authenticated: | |
| st.title("Petition Email Generator") | |
| st.subheader("Authentication Required") | |
| with st.form("auth_form"): | |
| password = st.text_input("Enter password", type="password") | |
| submitted = st.form_submit_button("Login") | |
| if submitted: | |
| if password == PASSWORD_TOKEN: | |
| st.session_state.is_authenticated = True | |
| st.rerun() # Refresh the page after successful login | |
| else: | |
| st.error("Incorrect password") | |
| else: | |
| # Title and description | |
| st.title("Petition Email Generator") | |
| st.markdown(""" | |
| This app helps you create a personalized email to support a Petition. | |
| Follow the steps below to generate your unique email. | |
| """) | |
| # Tips section moved from footer to top | |
| st.markdown(""" | |
| ### Tips for Effective Emails | |
| - Be polite and respectful | |
| - Clearly state your support for the petition | |
| - Share personal experiences if relevant | |
| - Thank the recipient for their time and consideration | |
| """) | |
| st.markdown("---") | |
| # Initialize session state for storing the generated email | |
| if 'generated_email' not in st.session_state: | |
| st.session_state.generated_email = "" | |
| if 'example_generated' not in st.session_state: | |
| st.session_state.example_generated = False | |
| if 'generated_subject' not in st.session_state: | |
| st.session_state.generated_subject = "" | |
| # Only access environment variables if authenticated | |
| if st.session_state.is_authenticated: | |
| # Set API key from environment variable | |
| openai.api_key = os.environ.get("OPENAI_API_KEY") | |
| # Load email examples | |
| email_examples = load_email_examples() | |
| # Fallback to environment variable if encryption doesn't work | |
| example_email = os.environ.get("EXAMPLE_EMAIL") | |
| # Get recipient email addresses | |
| to_emails = os.environ.get("TO_EMAILS", "") | |
| else: | |
| # If not authenticated, don't set API key | |
| openai.api_key = None | |
| email_examples = None | |
| example_email = "Authentication required to access the example email template." | |
| to_emails = "" | |
| # Function to generate email with subject (only works when authenticated) | |
| def generate_email(name, location, feedback="", model=os.environ.get("MODEL", "gpt-4.1-nano")): | |
| # Check authentication | |
| if not st.session_state.is_authenticated: | |
| st.error("Authentication required. Please use the correct password in the URL.") | |
| return None, None | |
| try: | |
| # If we have loaded examples, use them for a more varied prompt | |
| if email_examples: | |
| # Either pick a random full example email | |
| if random.random() < 0.3: # 30% chance to use a complete example | |
| selected_example = random.choice(email_examples["example_emails"]) | |
| example_body = selected_example["full_email"] | |
| # Or compose a new one from modular blocks | |
| else: | |
| blocks = email_examples["modular_blocks"] | |
| selected_blocks = { | |
| category: random.choice(blocks[category]) | |
| for category in blocks.keys() | |
| } | |
| # Build a coherent email from the selected blocks | |
| example_body = "\n\n".join([ | |
| selected_blocks["intro"], | |
| selected_blocks["concern"], | |
| selected_blocks["argument"], | |
| selected_blocks["support"], | |
| selected_blocks["closing"] | |
| ]) | |
| # Use the constructed example | |
| base_example = example_body | |
| else: | |
| # Fallback to the environment variable example | |
| base_example = example_email | |
| print("BASE_EXAMPLE:") | |
| print(base_example) | |
| prompt = f""" | |
| Generate a personalized and unique email supporting the Petition based on this example: | |
| ALWAYS start the email with "Dear Commissioners" | |
| {base_example} | |
| Customize it for: | |
| - Name of sender: {name} | |
| - Location of sender: {location} | |
| Additional preferences: | |
| {feedback if feedback else "None provided."} | |
| Return BOTH: | |
| 1. A concise, compelling subject line (one line only) | |
| 2. The email body text | |
| Format your response exactly as follows: | |
| SUBJECT: [Your generated subject line here] | |
| [Your generated email body here] | |
| Make sure the email is unique, professional, and persuasive. | |
| """ | |
| response = openai.chat.completions.create( | |
| model=model, | |
| messages=[ | |
| {"role": "system", "content": "You are a helpful assistant that creates personalized emails."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| temperature=0.7, | |
| max_tokens=800 | |
| ) | |
| content = response.choices[0].message.content | |
| # Extract subject and body from the response | |
| if "SUBJECT:" in content: | |
| parts = content.split("\n\n", 1) | |
| subject = parts[0].replace("SUBJECT:", "").strip() | |
| body = parts[1] if len(parts) > 1 else "" | |
| return subject, body | |
| else: | |
| # Fallback if format wasn't followed | |
| return "Support for the Petition", content | |
| except Exception as e: | |
| st.error(f"Error generating email: {str(e)}") | |
| return None, None | |
| # Main content with two columns | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| st.subheader("π Steps to Generate Your Email") | |
| # Step 1: Enter Your Information | |
| info_step = st.expander("Step 1: Enter Your Information", expanded=True) | |
| with info_step: | |
| user_name = st.text_input("Your Name") | |
| location = st.text_input("Your City or County") | |
| if user_name and location: | |
| st.success("Information provided! β ") | |
| # Step 2: Additional Preferences (Optional) | |
| pref_step = st.expander("Step 2: Customize Your Email (Optional)", expanded=True) | |
| with pref_step: | |
| additional_feedback = st.text_area( | |
| "Additional details or preferences for your email", | |
| placeholder="E.g., I'd like to emphasize economic benefits, include personal stories, etc." | |
| ) | |
| if additional_feedback: | |
| st.success("Customization added! β ") | |
| # Step 3: Generate Email | |
| generate_step = st.expander("Step 3: Generate Your Email", expanded=True) | |
| with generate_step: | |
| # Disable button if not authenticated or missing required fields | |
| generate_button = st.button( | |
| "Generate Email", | |
| type="primary", | |
| disabled=(not st.session_state.is_authenticated) or (not user_name or not location) | |
| ) | |
| if not st.session_state.is_authenticated: | |
| st.warning("Authentication required to generate emails.") | |
| elif not user_name or not location: | |
| st.warning("Please complete Step 1 (Your Information) first.") | |
| with col2: | |
| st.subheader("π¨ Your Personalized Email") | |
| # Only attempt to pre-generate example if authenticated | |
| if st.session_state.is_authenticated and not st.session_state.example_generated and not generate_button: | |
| with st.spinner("Generating an example email..."): | |
| example_subject, example_body = generate_email("Jane Smith", "Springfield County") | |
| if example_body: | |
| st.session_state.generated_subject = example_subject | |
| st.session_state.generated_email = example_body | |
| st.session_state.example_generated = True | |
| st.info("Here's an example of a generated email. Customize it by entering your information and preferences in the steps on the left.") | |
| elif not st.session_state.is_authenticated: | |
| st.info("Authentication required to generate example emails.") | |
| # Handle manual generation | |
| if generate_button and user_name and location: | |
| with st.spinner("Generating your personalized email..."): | |
| generated_subject, generated_body = generate_email(user_name, location, additional_feedback) | |
| if generated_body: | |
| st.session_state.generated_subject = generated_subject | |
| st.session_state.generated_email = generated_body | |
| st.success("Your personalized email has been generated! β ") | |
| # Display recipient email addresses if available | |
| if to_emails: | |
| st.subheader("π§ Send your email to:") | |
| email_col, copy_email_col = st.columns([9, 1]) | |
| with email_col: | |
| st.code(to_emails, language=None) | |
| with copy_email_col: | |
| st.markdown('<div style="display: flex; align-items: center; height: 100%;">', unsafe_allow_html=True) | |
| copy_button(to_emails) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| # Display the subject line (if available) | |
| if st.session_state.generated_subject: | |
| subject_container = st.container() | |
| with subject_container: | |
| st.subheader("Subject Line:") | |
| subject_col, copy_subject_col = st.columns([9, 1]) | |
| with subject_col: | |
| subject_text = st.text_input( | |
| "", | |
| value=st.session_state.generated_subject, | |
| label_visibility="collapsed" | |
| ) | |
| with copy_subject_col: | |
| copy_button(subject_text) | |
| # Display the email body (either pre-generated or manually generated) | |
| email_container = st.container() | |
| with email_container: | |
| st.subheader("Email Body:") | |
| body_col, copy_body_col = st.columns([9, 1]) | |
| with body_col: | |
| email_text = st.text_area( | |
| "", | |
| value=st.session_state.generated_email, | |
| height=350, | |
| label_visibility="collapsed" | |
| ) | |
| with copy_body_col: | |
| copy_button(st.session_state.generated_email) |