# retoor <retoor@molodetz.nl>
import re
from devplacepy.rendering import _render_content
from devplacepy.seo import truncate
from devplacepy.utils import strip_html
TITLE_MAX = 60
DESCRIPTION_MAX = 160
KEYWORDS_MAX = 8
_STOPWORDS = {
"the", "a", "an", "and", "or", "but", "of", "to", "in", "on", "for", "with",
"at", "by", "from", "as", "is", "are", "was", "were", "be", "been", "this",
"that", "these", "those", "it", "its", "into", "about", "over", "your",
"you", "we", "our", "i", "my", "me", "they", "them", "their", "he", "she",
"his", "her", "not", "no", "yes", "can", "will", "would", "should", "could",
"has", "have", "had", "do", "does", "did", "so", "if", "than", "then",
"there", "here", "what", "which", "who", "when", "where", "why", "how",
"all", "any", "some", "more", "most", "other", "such", "only", "own",
"just", "also", "very", "too", "out", "up", "down", "off", "now", "new",
}
_WORD_RE = re.compile(r"[a-z0-9][a-z0-9\+\#\.\-]{1,}")
def plain_text_from_markdown(text) -> str:
if not text:
return ""
rendered = _render_content(str(text))
return strip_html(rendered)
def _clamp_title(title: str) -> str:
title = " ".join((title or "").replace("\n", " ").split())
if len(title) <= TITLE_MAX:
return title
clipped = title[:TITLE_MAX].rsplit(" ", 1)[0]
return clipped or title[:TITLE_MAX]
def _clamp_description(description: str) -> str:
return truncate(" ".join((description or "").split()), DESCRIPTION_MAX)
def derive_keywords(title: str, body: str, limit: int = KEYWORDS_MAX) -> str:
text = f"{title or ''} {body or ''}".lower()
seen: list[str] = []
for match in _WORD_RE.finditer(text):
word = match.group(0).strip(".-")
if len(word) < 3 or word in _STOPWORDS or word in seen:
continue
seen.append(word)
if len(seen) >= limit:
break
return ", ".join(seen)
def plain_seo_defaults(title, body) -> dict:
clean_title = _clamp_title(plain_text_from_markdown(title) or (str(title or "")))
body_text = plain_text_from_markdown(body)
description = _clamp_description(body_text or clean_title)
keywords = derive_keywords(clean_title, body_text)
return {
"title": clean_title or "DevPlace",
"description": description or clean_title or "DevPlace",
"keywords": keywords,
}
def clamp_generated(title: str, description: str, keywords: str) -> dict:
clean_title = _clamp_title(plain_text_from_markdown(title) or title)
clean_description = _clamp_description(plain_text_from_markdown(description) or description)
terms = [
term.strip()
for term in re.split(r"[,\n;]+", keywords or "")
if term.strip()
]
cleaned: list[str] = []
for term in terms:
lowered = " ".join(term.lower().split())
if lowered and lowered not in cleaned:
cleaned.append(lowered)
if len(cleaned) >= KEYWORDS_MAX:
break
return {
"title": clean_title,
"description": clean_description,
"keywords": ", ".join(cleaned),
}