add admin page to manage private data

This commit is contained in:
Carl Zhang
2025-10-13 23:50:53 -05:00
parent c4066ed15e
commit 421d263b6b
9 changed files with 289 additions and 3 deletions

View File

@@ -0,0 +1,68 @@
import smtplib
import io
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
def send_email_with_attachment(sender_email, password, to_email, subject, image_bytes):
"""
Sends an email with an image attachment.
Args:
sender_email (str): The sender's email address.
password (str): The sender's email password.
to_email (str): The recipient's email address.
subject (str): The subject of the email.
image_bytes (bytes): The image content as a byte string.
"""
message = MIMEMultipart('alternative')
message['Subject'] = subject
message['From'] = sender_email
message['To'] = to_email
html_content = """
<html>
<head>
<title>Happy Halloween!</title>
</head>
<body style="font-family: Arial, sans-serif; background-color: #1a1a1a; color: #f0f0f0; margin: 0; padding: 20px; text-align: center;">
<div style="max-width: 600px; margin: auto; background-color: #2c2c2c; padding: 30px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.5);">
<h2 style="color: #ff6600;">🎃 Happy Halloween! 🎃</h2>
<p style="font-size: 16px; line-height: 1.6;">
Thank you for your interest in the
<a href="https://buffteks.org" target="_blank" rel="noopener" style="color: #ff8533; text-decoration: none;" onmouseover="this.style.textDecoration='underline';" onmouseout="this.style.textDecoration='none';">BuffTeks</a>
student organization!
</p>
<p style="font-size: 16px; line-height: 1.6;">
Wishing you a spooktacular Halloween!<br>
Here is your special holiday photo generated just for you.<br>
Enjoy and have a frightfully fun day!
</p>
</div>
</body>
</html>
"""
message.attach(MIMEText(html_content, 'html'))
if image_bytes:
# The image object is converted to bytes before attaching.
img_byte_arr = io.BytesIO()
# Assuming image_bytes is a PIL Image object.
# Convert to RGB if necessary for JPEG format.
if image_bytes.mode in ('RGBA', 'P'):
image_bytes = image_bytes.convert('RGB')
image_bytes.save(img_byte_arr, format='JPEG')
img_byte_arr = img_byte_arr.getvalue()
image_part = MIMEImage(img_byte_arr, _subtype="jpeg")
image_part.add_header('Content-Disposition', 'attachment', filename="halloween.jpg")
message.attach(image_part)
try:
with smtplib.SMTP('mail.privateemail.com', 587) as server:
server.starttls()
server.login(sender_email, password)
server.send_message(message)
print("Email sent successfully!")
except Exception as e:
print(f"Failed to send email: {str(e)}")

View File

@@ -0,0 +1,123 @@
import streamlit as st
from PIL import Image
from google import genai
import json
from io import BytesIO
from .email_helloween_image import send_email_with_attachment
def camera_photo():
st.markdown("<h1 style='text-align: center; color: #451002;'>🎃 Halloween Photo Booth 📸</h1>", unsafe_allow_html=True)
st.markdown("<h5 style='text-align: center;'> Capture your Halloween spirit with a festive photo! 👻 </h3>", unsafe_allow_html=True)
if 'image_response' not in st.session_state:
st.session_state.image_response = None
enable_camera = st.checkbox("Enable Camera")
if enable_camera:
st.info("📎Note: No photos are stored. The image is processed in-memory and deleted after refresh app.")
picture = st.camera_input("Take a Halloween-themed photo! 🎃👻🕸️")
if picture is not None:
image = Image.open(picture)
keep_dressing_style = st.checkbox("Keep original dressing style", value=False)
keep_body_pose = st.checkbox("Keep original body pose", value=False)
theme = st.selectbox(
"Select or Input a Halloween theme for your photo:",
[ "🎃 Classic Halloween",
"👻 Haunted House",
"⚗️ Science Experiment Gone Wrong",
"🎥 Horror Film Inspired",
"🕯️ Gothic",
"🧙‍♂️ Harry Potter",
"⚰️ Graveyard",
"🔮 Cult Horror Theme"
],
index=0,
key="theme_selector"
)
if st.button("AI Editing"):
edit_prompt = f"""
Retain original facial features (eyes, nose, mouth etc.) of any persons in the photo. The goal is to achieve a realistic, edited look, not an AI-generated or overly smoothed/perfected appearance. Avoid any artificial alterations to these features.
Keep the original dressing style: {str(keep_dressing_style)}
Keep the original body pose: {str(keep_body_pose)}
Theme: {theme}
If a person is already in costume, enhance the costume and add more Halloween elements and background to the scene.
If multiple persons are present, ensure all are included in the Halloween theme.
- Overall Style: The final image should be photorealistic.
Ensure the final image is vibrant, festive, and captures the Halloween spirit!
"""
with st.spinner("Editing image..."):
text_response, image_response = edit_image_with_ai(image, edit_prompt)
if text_response:
st.info(text_response)
if image_response:
st.session_state.image_response = image_response
# Rerun to display the generated image and send options
st.rerun()
if st.session_state.image_response:
st.image(st.session_state.image_response)
# Convert PIL image to bytes for download
img_byte_arr = BytesIO()
st.session_state.image_response.save(img_byte_arr, format="JPEG")
st.download_button(
label="Download Edited Image",
data=img_byte_arr.getvalue(),
file_name="edited_image.jpeg",
mime="image/jpeg"
)
send_image(st.session_state.image_response)
def send_image(image_response):
st.divider()
to_email = st.text_input("Enter your email to receive the photo:", key="email_input")
if st.button("Send Email"):
with st.spinner("Sending email..."):
if to_email and image_response:
with open('app_config.json') as config_file:
config = json.load(config_file)
sender_email = config["send_email"]["sender_email"]
password = config["send_email"]["password"]
subject = "Your Spooktacular Halloween Photo! 🎃👻"
send_email_with_attachment(sender_email, password, to_email, subject, image_response)
st.success("Email sent successfully!")
else:
st.error("Please enter a valid email address and ensure the image is generated.")
def edit_image_with_ai(image, description):
with open('app_config.json') as config_file:
config = json.load(config_file)
api_key = config["nano-banana"]["api_key"]
client = genai.Client(api_key=api_key)
prompt = description
response = client.models.generate_content(
model="gemini-2.5-flash-image-preview",
contents=[prompt, image],
)
text_response = None
image_response = None
# AttributeError: 'NoneType' object has no attribute 'parts'
if response.candidates and len(response.candidates) <= 0:
st.error("No response from AI model. Please try again.")
return text_response, image_response
for part in response.candidates[0].content.parts:
if part.text is not None:
text_response = part.text
if part.inline_data is not None:
image_response = Image.open(BytesIO(part.inline_data.data))
return text_response, image_response