diff --git a/app/models.py b/app/models.py index 7384e22..6b04a6d 100644 --- a/app/models.py +++ b/app/models.py @@ -14,10 +14,10 @@ class Attendee(db.Model): phone = db.Column(db.String(15), nullable=False) dietary_restrictions = db.Column(db.String(255)) created_at = db.Column( - db.DateTime, server_default=db.text("current_timestamp():::TIMESTAMPTZ") + db.DateTime, server_default=db.text("current_timestamp::TIMESTAMPTZ") ) updated_at = db.Column( - db.DateTime, server_default=db.text("current_timestamp():::TIMESTAMPTZ") + db.DateTime, server_default=db.text("current_timestamp::TIMESTAMPTZ") ) invited = db.Column(db.Boolean, nullable=False, server_default=db.text("true")) @@ -35,10 +35,10 @@ class Event(db.Model): location = db.Column(db.String(600), nullable=False) description = db.Column(db.String(1000), nullable=False) created_at = db.Column( - db.DateTime, server_default=db.text("current_timestamp():::TIMESTAMPTZ") + db.DateTime, server_default=db.text("current_timestamp::TIMESTAMPTZ") ) updated_at = db.Column( - db.DateTime, server_default=db.text("current_timestamp():::TIMESTAMPTZ") + db.DateTime, server_default=db.text("current_timestamp::TIMESTAMPTZ") ) image_id = db.Column(db.String(30), nullable=False) @@ -53,12 +53,12 @@ class EventAttendeeJunction(db.Model): public_id = db.Column(db.String(12), nullable=False) event_id = db.Column(db.ForeignKey("events.id", ondelete="CASCADE")) attendee_id = db.Column(db.ForeignKey("attendee.id", onupdate="CASCADE")) - rsvp = db.Column(NullType) + rsvp = db.Column(db.String(255)) created_at = db.Column( - db.DateTime, server_default=db.text("current_timestamp():::TIMESTAMPTZ") + db.DateTime, server_default=db.text("current_timestamp::TIMESTAMPTZ") ) updated_at = db.Column( - db.DateTime, server_default=db.text("current_timestamp():::TIMESTAMPTZ") + db.DateTime, server_default=db.text("current_timestamp::TIMESTAMPTZ") ) attendee = relationship("Attendee") diff --git a/app/templates/attendee_rsvp.html b/app/templates/attendee_rsvp.html index bdf864f..a81461e 100644 --- a/app/templates/attendee_rsvp.html +++ b/app/templates/attendee_rsvp.html @@ -1,4 +1,8 @@ {% extends "base.html" %}{%block coverimage %}{{event.image_id}}{% endblock %} + +{% block og_image %}{{ url_for('static', filename='images/og/rsvp_' ~ event_junction_public_id ~ '.png') }}{% endblock %} +{% block twitter_image %}{{ url_for('static', filename='images/og/rsvp_' ~ event_junction_public_id ~ '.png') }}{% endblock %} + {% block content %}
diff --git a/app/templates/base.html b/app/templates/base.html index 068fc68..c4537bb 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -26,7 +26,7 @@ /> @@ -40,7 +40,7 @@ /> {% if title %} diff --git a/freeze.py b/freeze.py index 9653add..0948f6a 100644 --- a/freeze.py +++ b/freeze.py @@ -134,12 +134,81 @@ def attendee_rsvp(): def not_found_page(): yield {} +def generate_og_images(): + """Generate custom Open Graph images for all active RSVPs""" + from PIL import ImageDraw, ImageFont + + og_dir = Path(app.static_folder) / 'images' / 'og' + og_dir.mkdir(parents=True, exist_ok=True) + + font_path = Path(app.static_folder) / 'font' / 'LCDBlock.ttf' + try: + title_font = ImageFont.truetype(str(font_path), 50) + subtitle_font = ImageFont.truetype(str(font_path), 35) + except: + title_font = ImageFont.load_default() + subtitle_font = ImageFont.load_default() + + utc = pytz.UTC + now = datetime.now(utc) + time_diff = timedelta(days=1) + + active_events = Event.query.filter(Event.date >= now - time_diff).all() + + for event in active_events: + rsvps = ( + EventAttendeeJunction.query + .join(Attendee) + .filter(EventAttendeeJunction.event_id == event.id) + .filter(Attendee.invited == True) + .all() + ) + for rsvp in rsvps: + out_path = og_dir / f"rsvp_{rsvp.public_id}.png" + if out_path.exists(): + continue + + base_img_path = Path(app.static_folder) / 'images' / 'image.png' + if base_img_path.exists(): + try: + img = Image.open(base_img_path).convert("RGBA").resize((1200, 630)) + overlay = Image.new('RGBA', img.size, (0, 0, 0, 160)) + img = Image.alpha_composite(img, overlay).convert("RGB") + except: + img = Image.new('RGB', (1200, 630), color=(10, 10, 10)) + else: + img = Image.new('RGB', (1200, 630), color=(10, 10, 10)) + + draw = ImageDraw.Draw(img) + + attendee_name = rsvp.attendee.attendee if rsvp.attendee.attendee else "Guest" + + text = f"You're invited, {attendee_name}!" + draw.text((100, 120), text, font=title_font, fill=(255, 255, 255)) + + event_text = f"Event: {event.event}" + draw.text((100, 240), event_text, font=subtitle_font, fill=(200, 200, 200)) + + date_str = event.date.strftime("%B %d, %Y") + date_text = f"Date: {date_str}" + draw.text((100, 320), date_text, font=subtitle_font, fill=(200, 200, 200)) + + loc_text = f"Location: {event.location}" + draw.text((100, 400), loc_text, font=subtitle_font, fill=(200, 200, 200)) + + img.save(out_path) + print(f"Generated OG image for {attendee_name} (RSVP {rsvp.public_id})") if __name__ == "__main__": # Cache images before freezing with app.app_context(): + print("Caching event images...") cache_event_images() print() + + print("Generating OG images...") + generate_og_images() + print() freezer.freeze()