polygrapher / app /templateify_base.py
dhruv575
Update and clean
c4a298e
"""Shared helpers for templateify services (daily + special events)."""
import logging
from typing import Callable, Dict, List
from bs4 import BeautifulSoup
from bs4.element import NavigableString, Tag
logger = logging.getLogger(__name__)
class TemplateifyHelpers:
"""Holds soup/tokens state and provides shared templateify operations."""
def __init__(self, soup: BeautifulSoup) -> None:
self.soup = soup
self.tokens: List[Dict[str, str]] = []
self._seen: set = set()
def register(self, name: str, description: str) -> None:
if name not in self._seen:
self.tokens.append({"name": name, "description": description})
self._seen.add(name)
def set_text(self, selector: str, token: str, description: str) -> None:
node = self.soup.select_one(selector)
if not node:
return
node.clear()
node.append(token)
self.register(token, description)
def set_attr(self, parent: Tag, selector: str, attr: str, token: str, description: str) -> None:
node = parent.select_one(selector)
if not node:
return
node[attr] = token
self.register(token, description)
def set_child_text(self, parent: Tag, selector: str, token: str, description: str) -> None:
node = parent.select_one(selector)
if not node:
return
node.clear()
node.append(token)
self.register(token, description)
def loopify(self, selector: str, loop_name: str, transformer: Callable[[Tag], None], description: str) -> None:
nodes = self.soup.select(selector)
if not nodes:
return
template_node = nodes[0]
for extra in nodes[1:]:
extra.decompose()
transformer(template_node)
self.register(loop_name, description)
# Determine which element to wrap (the node, or its parent anchor)
if template_node.parent and template_node.parent.name == "a":
target = template_node.parent
else:
target = template_node
parent = target.parent
if not parent:
logger.warning(f"Template node {selector} has no parent, cannot wrap in loop")
return
try:
idx = parent.index(target)
parent.insert(idx, NavigableString(f"{{{{#{loop_name}}}}}"))
parent.insert(idx + 2, NavigableString(f"{{{{/{loop_name}}}}}"))
except ValueError:
logger.warning(f"Could not find index for {selector}, node may have been moved")
def wrap_images_in_anchors(self, header_alt: str = "Polygraph by Polymarket", footer_alt: str = "Polymarket", link_url: str = "https://poly.market/0d7VvzI") -> None:
"""Wrap header and footer logo images in anchor tags if not already linked."""
for alt_text in (header_alt, footer_alt):
for table in self.soup.find_all("table", role="presentation"):
img = table.find("img", alt=alt_text)
if img and not img.find_parent("a"):
link = self.soup.new_tag("a", href=link_url, target="_blank", rel="noopener noreferrer")
img.wrap(link)
break