Source code for help

import re
from pathlib import Path

from flask import (
    request,
    render_template,
)
from markdown_it import MarkdownIt
from markupsafe import Markup
from bs4 import BeautifulSoup
from werkzeug.exceptions import NotFound


[docs] class HelpPages: def __init__(self):
[docs] self.templates_dir = Path('app/view/templates')
[docs] self.root_dir = self.templates_dir / Path('pages/help/pages')
[docs] self.markdown = MarkdownIt().enable('table')
[docs] self._html_cache = {}
[docs] self._text_cache = {}
[docs] def render_html(self, base_path): try: return self._html_cache[base_path] except KeyError as e: raise NotFound() from e
[docs] def search(self, query): results = [] search_pattern = '(' + re.escape(query) + ')' regex = re.compile(search_pattern, re.IGNORECASE) for name, text in self._text_cache.items(): excerpts = [] ranges = [] for match in re.finditer(regex, text): start_index = max(match.start() - 30, 0) end_index = min(match.end() + 30, len(text)) if len(ranges) > 0 and start_index in ranges[-1]: # Merge with previous index: ranges[-1] = range(ranges[-1].start, end_index) else: ranges.append(range(start_index, end_index)) excerpts = [] for excerpt in [text[r.start:r.stop] for r in ranges]: # Remove possibly partial first/last word, add truncation: if ranges[0].start > 0: excerpt = re.sub(r'^[a-zA-Z.?!,:]*\s*', '', excerpt) if ranges[-1].stop < len(text): excerpt = re.sub(r'\s*[a-zA-Z.?!,:]*$', '', excerpt) excerpts.append(excerpt) if excerpts: full_excerpt = ' [...] '.join(excerpts) if ranges[0].start > 0: full_excerpt = '...' + full_excerpt if ranges[-1].stop < len(text): full_excerpt = full_excerpt + '...' results.append({ 'name': name, 'excerpt_html': re.sub(regex, r'<span class="highlight">\1</span>', full_excerpt), }) return results
[docs] def process_once(self): if self._html_cache: return for file in self.root_dir.iterdir(): extension = file.suffix base_path = str(file).removesuffix(extension).removeprefix(str(self.templates_dir) + '/') base_key = str(file).removesuffix(extension).removeprefix(str(self.root_dir) + '/') if extension == '.md': markdown_content = render_template(f"{base_path}.md") html_content = Markup(self.markdown.render(markdown_content)) elif extension == '.html': html_content = Markup(render_template(f"{base_path}.html")) else: html_content = None if html_content: self._html_cache[base_key] = html_content soup = BeautifulSoup(html_content, 'html.parser') self._text_cache[base_key] = ' '.join(soup.get_text(' ', strip=True).split())
[docs] HELP_PAGES = HelpPages()
[docs] def help_index_page(): HELP_PAGES.process_once() if len(request.args.get('query', '')) >= 3: search_results = HELP_PAGES.search(request.args['query']) else: search_results = None if _request_is_ajax(): return render_template( "pages/help/_page_list.html", search_results=search_results, ) else: return render_template( "pages/help/index.html", search_results=search_results, )
[docs] def help_show_page(name): HELP_PAGES.process_once() title = name.replace('-', ' ').title() html_content = HELP_PAGES.render_html(name) return render_template( "pages/help/show.html", title=title, content=html_content, )
[docs] def _request_is_ajax(): return request.headers.get('X-Requested-With', '') == 'XMLHttpRequest'