vj4/util/misc.py

Summary

Maintainability
A
1 hr
Test Coverage
import base64
import hashlib
import jinja2
import markupsafe
import misaka
import re
from urllib import parse

from vj4.util import options


MARKDOWN_EXTENSIONS = (misaka.EXT_TABLES |  # Parse PHP-Markdown style tables.
                       misaka.EXT_FENCED_CODE |  # Parse fenced code blocks.
                       misaka.EXT_AUTOLINK |  # Automatically turn safe URLs into links.
                       misaka.EXT_NO_INTRA_EMPHASIS |  # Disable emphasis_between_words.
                       misaka.EXT_MATH |  # Parse TeX $$math$$ syntax, Kramdown style.
                       misaka.EXT_SPACE_HEADERS |  # Require a space after '#' in headers.
                       misaka.EXT_MATH_EXPLICIT |  # Instead of guessing by context, parse $inline math$ and $$always block math$$ (requires EXT_MATH).
                       misaka.EXT_DISABLE_INDENTED_CODE)  # Don't parse indented code blocks.
MARKDOWN_RENDER_FLAGS = (misaka.HTML_ESCAPE |  # Escape all HTML.
                         misaka.HTML_HARD_WRAP)  # Render each linebreak as <br>.


FS_RE = re.compile(r'\(vijos\:\/\/fs\/([0-9a-f]{40,})\)')


def nl2br(text):
  markup = markupsafe.escape(text)
  return markupsafe.Markup('<br>'.join(markup.split('\n')))


def fs_replace(m):
  # TODO(twd2): reverse_url
  return '(' + options.cdn_prefix.rstrip('/') + '/fs/' + m.group(1) + ')'


def markdown(text):
  text = FS_RE.sub(fs_replace, text)
  return markupsafe.Markup(misaka.html(
      text, extensions=MARKDOWN_EXTENSIONS, render_flags=MARKDOWN_RENDER_FLAGS))


def gravatar_url(gravatar, size=200):
  # TODO: 'd' should be https://domain/img/avatar.png
  if gravatar:
    gravatar_hash = hashlib.md5(gravatar.lower().encode()).hexdigest()
  else:
    gravatar_hash = ''
  return ('//cn.gravatar.com/avatar/' + gravatar_hash + "?" +
          parse.urlencode({'d': 'mm', 's': str(size)}))


def paginate(page, num_pages):
  radius = 2
  if page > 1:
    yield 'first', 1
    yield 'previous', page - 1
  if page <= radius:
    first, last = 1, min(1 + radius * 2, num_pages)
  elif page >= num_pages - radius:
    first, last = max(1, num_pages - radius * 2), num_pages
  else:
    first, last = page - radius, page + radius
  if first > 1:
    yield 'ellipsis', 0
  for page0 in range(first, last + 1):
    if page0 != page:
      yield 'page', page0
    else:
      yield 'current', page
  if last < num_pages:
    yield 'ellipsis', 0
  if page < num_pages:
    yield 'next', page + 1
    yield 'last', num_pages


def format_size(size, base=1, ndigits=3):
  size *= base
  unit = 1024
  unit_names = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
  for unit_name in unit_names:
    if size < unit:
      return '{0} {1}'.format(round(size, ndigits=ndigits), unit_name)
    size /= unit
  return '{0} {1}'.format(round(size * unit, ndigits=ndigits), unit_names[-1])


def format_seconds(seconds):
  seconds = int(seconds)
  return '{:02}:{:02}:{:02}'.format(seconds // 3600, seconds % 3600 // 60, seconds % 60)


def base64_encode(str):
  encoded = base64.b64encode(str.encode())
  return encoded.decode()


def dedupe(list):
  result = []
  result_set = set()
  for i in list:
    if i in result_set:
      continue
    result.append(i)
    result_set.add(i)
  return result