Files
ad/molodetz/seo.py
T

118 lines
3.6 KiB
Python

# retoor <retoor@molodetz.nl>
import json
import re
from molodetz import config
from molodetz.assets import static_url
from molodetz.seo_meta_text import DEFAULT_DESCRIPTION
DEFAULT_OG_IMAGE_PATH = "/static/media/flyer-elite-twice.jpg"
def base_url(request):
if config.SITE_URL:
return config.SITE_URL
return str(request.base_url).rstrip("/")
def _clean(text):
return re.sub(r"[*_`#\[\]]", "", text or "").strip()
def website_schema(request):
base = base_url(request)
return {
"@context": "https://schema.org",
"@type": "WebSite",
"name": config.APP_TITLE,
"url": base + "/",
"inLanguage": "en",
"potentialAction": {
"@type": "SearchAction",
"target": base + "/docs/search?q={search_term_string}",
"query-input": "required name=search_term_string",
},
}
def organization_schema(request):
base = base_url(request)
return {"@context": "https://schema.org", "@type": "Organization", "name": config.APP_TITLE, "url": base + "/"}
def breadcrumb_schema(request, breadcrumbs):
base = base_url(request)
return {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{"@type": "ListItem", "position": index + 1, "name": _clean(name), "item": base + path}
for index, (name, path) in enumerate(breadcrumbs)
],
}
def article_schema(request, post, author):
base = base_url(request)
return {
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": _clean(post["title"]),
"datePublished": post.get("published_at") or post.get("created_at"),
"dateModified": post.get("updated_at"),
"inLanguage": "en",
"mainEntityOfPage": f"{base}/posts/{post['slug']}",
"author": {"@type": "Person", "name": author["username"] if author else "retoor", "url": f"{base}/people/{author['username'] if author else 'retoor'}"},
"publisher": {"@type": "Organization", "name": config.APP_TITLE},
}
def profile_schema(request, person):
base = base_url(request)
return {
"@context": "https://schema.org",
"@type": "ProfilePage",
"mainEntity": {"@type": "Person", "name": person["username"], "url": f"{base}/people/{person['username']}"},
}
def base_seo_context(
request,
title,
description=None,
robots="index,follow",
og_type="website",
og_image=None,
breadcrumbs=None,
schemas=None,
prev_url=None,
next_url=None,
keywords=None,
):
base = base_url(request)
canonical = base + request.url.path
page = request.query_params.get("page")
if page:
canonical += f"?page={page}"
image = og_image or static_url(DEFAULT_OG_IMAGE_PATH)
if image.startswith("/"):
image = base + image
crumbs = [(_clean(name), path) for name, path in (breadcrumbs or [])]
all_schemas = list(schemas or [])
if crumbs:
all_schemas.append(breadcrumb_schema(request, crumbs))
full_title = f"{title} | {config.APP_TITLE}" if title and title != config.APP_TITLE else config.APP_TITLE
return {
"title": full_title,
"description": description or DEFAULT_DESCRIPTION,
"robots": robots,
"canonical": canonical,
"og_type": og_type,
"og_image": image,
"breadcrumbs": crumbs,
"json_ld": [json.dumps(schema, ensure_ascii=False).replace("</", "<\\/") for schema in all_schemas],
"prev_url": prev_url,
"next_url": next_url,
"keywords": keywords or "molodetz, writing, craftsmanship, software, community",
}