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
from urllib.parse import urlencode

import yaml
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 gettext as _
from pytz import timezone

from promgen import util

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(f'{k}!~"{v}"' for k, v in filters.items())) return clause.replace(EXCLUSION_MACRO, macro)
[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()
[docs] @register.simple_tag def urlqs(view, **kwargs): """ Query string aware version of url template Instead of using {% url 'view' %} Use {% urlqs 'view' param=value %} This is useful for linking to pages that use filters. This only works for views that do not need additional parameters """ return reverse(view) + "?" + urlencode(kwargs)