06/15 Phase 4 + 5 codes

This commit is contained in:
2026-06-15 17:43:47 -04:00
parent f57a95013c
commit a9efd15a76
36 changed files with 1677 additions and 57 deletions
View File
+69
View File
@@ -0,0 +1,69 @@
"""Ads blueprint: click tracking redirect, sponsor directory.
Ad creative upload lives in the admin blueprint (Phase 6).
"""
from flask import (Blueprint, redirect, abort, render_template,
request, current_app)
from flask_login import current_user
from flask_babel import get_locale
from app.extensions import db
from app.models.ads import Ad
from app.services.ads import (get_ad, record_impression, record_click,
active_sponsors)
ads_bp = Blueprint("ads", __name__)
# ── Click tracking redirect ────────────────────────────────────────────────
@ads_bp.route("/ads/<int:ad_id>/click")
def click(ad_id):
ad = db.session.get(Ad, ad_id)
if ad is None or not ad.is_running:
abort(404)
try:
record_click(ad_id)
except Exception as e:
current_app.logger.error("Click record error: %s", e)
return redirect(ad.target_url)
# ── Sponsor directory ──────────────────────────────────────────────────────
@ads_bp.route("/sponsors")
def sponsor_directory():
sponsors = active_sponsors(tier="directory")
return render_template("sponsors/directory.html", sponsors=sponsors)
# ── Context processor: inject ads into every template ─────────────────────
def inject_ads():
"""Called by the app factory context processor.
Returns ad slots for use in templates.
Ads suppressed for subscribers (ad_free plan limit).
"""
show_ads = True
if current_user.is_authenticated:
plan = current_user.tier
if plan and plan.limit("ad_free"):
show_ads = False
if not show_ads:
return {"ads": {}, "show_ads": False}
lang = str(get_locale()) if get_locale() else None
state = request.args.get("state") or None
ads = {}
for slot in Ad.SLOTS:
ad = get_ad(slot, lang=lang, state=state)
if ad:
ads[slot] = ad
# record impression (fire-and-forget; errors suppressed)
try:
record_impression(ad.id)
except Exception:
pass
return {"ads": ads, "show_ads": True}