Source code for promgen.templatetags.promgen

# Copyright (c) 2017 LINE Corporation
# These sources are released under the terms of the MIT license: see LICENSE

import collections
import difflib
import json
from datetime import datetime

import yaml
from pytz import timezone

from promgen import util

from django import template
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _

register = template.Library()

EXCLUSION_MACRO = '<exclude>'


[docs]@register.filter() def klass(value): return value.__class__.__name__
[docs]@register.filter() def rule_dict(rule): return { "alert": rule.name, "expr": rulemacro(rule), "for": rule.duration, "labels": rule.labels, "annotations": rule.annotations, }
[docs]@register.filter() def rulemacro(rule, clause=None): ''' Macro rule expansion Assuming a list of rules with children and parents, expand our macro to exclude child rules Can optionally pass expression to render in the context of the passed rule .. code-block:: none foo{<exclude>} / bar{<exclude>} > 5 # Parent Rule foo{project="A", <exclude>} / bar{project="A", <exclude>} > 3 # Child Rule foo{project="B"} / bar{project="B"} > 4 # Child Rule foo{project~="A|B"} / bar{project~="A|B"} > 5 foo{project="A", } / bar{project="A"} > 3 foo{project="B"} / bar{project="B"} > 4 ''' if not clause: clause = rule.clause labels = collections.defaultdict(list) for r in rule.overrides.all(): labels[r.content_type.model].append(r.content_object.name) filters = { k: '|'.join(labels[k]) for k in sorted(labels) } macro = ','.join( sorted('{}!~"{}"'.format(k, v) for k, v in filters.items()) ) return clause.replace(EXCLUSION_MACRO, macro)
[docs]@register.simple_tag def qsfilter(request, k, v): ''' Helper to rewrite query string for URLs {% qsfilter request 'foo' 'baz' %} When passed the request object, it will take a querystring like ?foo=bar&donottouch=1 and change it to ?foo=baz&donottouch=1 Useful when working with filtering on a page that also uses pagination to avoid losing other query strings {% qsfilter request 'page' page_obj.previous_page_number %} ''' dict_ = request.GET.copy() if v: dict_[k] = v else: dict_.pop(k, None) return dict_.urlencode()
[docs]@register.simple_tag def diff_json(a, b): if isinstance(a, str): a = json.loads(a) if isinstance(b, str): b = json.loads(b) a = json.dumps(a, indent=4, sort_keys=True).splitlines(keepends=True) b = json.dumps(b, indent=4, sort_keys=True).splitlines(keepends=True) diff = ''.join(difflib.unified_diff(a, b)) if diff: return diff return 'No Changes'
[docs]@register.filter() def pretty_json(data): if isinstance(data, str): data = json.loads(data) return json.dumps(data, indent=4, sort_keys=True)
[docs]@register.filter() def pretty_yaml(data): return yaml.safe_dump(data)
[docs]@register.filter() def strftime(timestamp, fmt): tz = util.setting("timezone", "UTC") if isinstance(timestamp, int) or isinstance(timestamp, float): return timezone(tz).localize(datetime.fromtimestamp(timestamp)).strftime(fmt) return timestamp
[docs]@register.simple_tag(takes_context=True) def qs_replace(context, k, v): """ Query string handler for paginators Assuming we have a query string like ?page=1&search=foo, there are several cases in which we want to replace only the page key, while leaving the rest alone. This tag allows us to replace individual values (like the current page) while carrying over other values (like a search string) Example: {% qs_replace 'page' page_obj.next_page_number %} """ dict_ = context["request"].GET.copy() if v: dict_[k] = v else: dict_.pop(k, None) return dict_.urlencode()