# Path: notifications_utils/formatters.py
# def escape_html(value):
#     if not value:
#         return value
#     value = str(value)
# 
#     for entity, temporary_replacement in HTML_ENTITY_MAPPING:
#         value = value.replace(entity, temporary_replacement)
# 
#     value = escape(unescape_strict(value), quote=False)
# 
#     for entity, temporary_replacement in HTML_ENTITY_MAPPING:
#         value = value.replace(temporary_replacement, entity)
# 
#     return value
# 
# def strip_and_remove_obscure_whitespace(value):
#     if value == '':
#         # Return early to avoid making multiple, slow calls to
#         # str.replace on an empty string
#         return ''
# 
#     for character in OBSCURE_ZERO_WIDTH_WHITESPACE + OBSCURE_FULL_WIDTH_WHITESPACE:
#         value = value.replace(character, '')
# 
#     return value.strip(string.whitespace)
# 
# def strip_html(value):
#     return bleach.clean(value, tags=[], strip=True)
# 
# def unescaped_formatted_list(
#     items,
#     conjunction='and',
#     before_each='‘',
#     after_each='’',
#     separator=', ',
#     prefix='',
#     prefix_plural=''
# ):
#     if prefix:
#         prefix += ' '
#     if prefix_plural:
#         prefix_plural += ' '
# 
#     if len(items) == 1:
#         return '{prefix}{before_each}{items[0]}{after_each}'.format(**locals())
#     elif items:
#         formatted_items = ['{}{}{}'.format(before_each, item, after_each) for item in items]
# 
#         first_items = separator.join(formatted_items[:-1])
#         last_item = formatted_items[-1]
#         return (
#             '{prefix_plural}{first_items} {conjunction} {last_item}'
#         ).format(**locals())
# 
# Path: notifications_utils/insensitive_dict.py
# class InsensitiveDict(OrderedDict):
# 
#     """
#     `InsensitiveDict` behaves like an ordered dictionary, except it normalises
#     case, whitespace, hypens and underscores in keys.
# 
#     In other words,
#     InsensitiveDict({'FIRST_NAME': 'example'}) == InsensitiveDict({'first name': 'example'})
#     >>> True
#     """
# 
#     KEY_TRANSLATION_TABLE = {
#         ord(c): None for c in ' _-'
#     }
# 
#     def __init__(self, row_dict):
#         for key, value in row_dict.items():
#             self[key] = value
# 
#     @classmethod
#     def from_keys(cls, keys):
#         """
#         This behaves like `dict.from_keys`, except:
#         - it normalises the keys to ignore case, whitespace, hypens and
#           underscores
#         - it stores the original, unnormalised key as the value of the
#           item so it can be retrieved later
#         """
#         return cls(OrderedDict([(key, key) for key in keys]))
# 
#     def keys(self):
#         return OrderedSet(super().keys())
# 
#     def __getitem__(self, key):
#         return super().__getitem__(self.make_key(key))
# 
#     def __setitem__(self, key, value):
#         super().__setitem__(self.make_key(key), value)
# 
#     def __contains__(self, key):
#         return super().__contains__(self.make_key(key))
# 
#     def get(self, key, default=None):
#         return self[key] if key in self else default
# 
#     def copy(self):
#         return self.__class__(super().copy())
# 
#     def as_dict_with_keys(self, keys):
#         return {
#             key: self.get(key) for key in keys
#         }
# 
#     @staticmethod
#     @lru_cache(maxsize=32, typed=False)
#     def make_key(original_key):
#         if original_key is None:
#             return None
#         return original_key.translate(InsensitiveDict.KEY_TRANSLATION_TABLE).lower()


import re
from markupsafe import Markup
from orderedset import OrderedSet
from notifications_utils.formatters import (
    escape_html,
    strip_and_remove_obscure_whitespace,
    strip_html,
    unescaped_formatted_list,
)
from notifications_utils.insensitive_dict import InsensitiveDict
    ):
        self.content = content
        self.values = values
        self.markdown_lists = markdown_lists
        if not with_brackets:
            self.placeholder_tag = self.placeholder_tag_no_brackets
        self.sanitizer = {
            'strip': strip_html,
            'escape': escape_html,
            'passthrough': str,
        }[html]
        self.redact_missing_personalisation = redact_missing_personalisation

    def __str__(self):
        if self.values:
            return self.replaced
        return self.formatted

    def __repr__(self):
        return "{}(\"{}\", {})".format(self.__class__.__name__, self.content, self.values)  # TODO: more real

    def splitlines(self):
        return str(self).splitlines()

    @property
    def values(self):
        return self._values

    @values.setter
    def values(self, value):

# Path: notifications_utils/formatters.py
# def escape_html(value):
#     if not value:
#         return value
#     value = str(value)
# 
#     for entity, temporary_replacement in HTML_ENTITY_MAPPING:
#         value = value.replace(entity, temporary_replacement)
# 
#     value = escape(unescape_strict(value), quote=False)
# 
#     for entity, temporary_replacement in HTML_ENTITY_MAPPING:
#         value = value.replace(temporary_replacement, entity)
# 
#     return value
# 
# def strip_and_remove_obscure_whitespace(value):
#     if value == '':
#         # Return early to avoid making multiple, slow calls to
#         # str.replace on an empty string
#         return ''
# 
#     for character in OBSCURE_ZERO_WIDTH_WHITESPACE + OBSCURE_FULL_WIDTH_WHITESPACE:
#         value = value.replace(character, '')
# 
#     return value.strip(string.whitespace)
# 
# def strip_html(value):
#     return bleach.clean(value, tags=[], strip=True)
# 
# def unescaped_formatted_list(
#     items,
#     conjunction='and',
#     before_each='‘',
#     after_each='’',
#     separator=', ',
#     prefix='',
#     prefix_plural=''
# ):
#     if prefix:
#         prefix += ' '
#     if prefix_plural:
#         prefix_plural += ' '
# 
#     if len(items) == 1:
#         return '{prefix}{before_each}{items[0]}{after_each}'.format(**locals())
#     elif items:
#         formatted_items = ['{}{}{}'.format(before_each, item, after_each) for item in items]
# 
#         first_items = separator.join(formatted_items[:-1])
#         last_item = formatted_items[-1]
#         return (
#             '{prefix_plural}{first_items} {conjunction} {last_item}'
#         ).format(**locals())
# 
# Path: notifications_utils/insensitive_dict.py
# class InsensitiveDict(OrderedDict):
# 
#     """
#     `InsensitiveDict` behaves like an ordered dictionary, except it normalises
#     case, whitespace, hypens and underscores in keys.
# 
#     In other words,
#     InsensitiveDict({'FIRST_NAME': 'example'}) == InsensitiveDict({'first name': 'example'})
#     >>> True
#     """
# 
#     KEY_TRANSLATION_TABLE = {
#         ord(c): None for c in ' _-'
#     }
# 
#     def __init__(self, row_dict):
#         for key, value in row_dict.items():
#             self[key] = value
# 
#     @classmethod
#     def from_keys(cls, keys):
#         """
#         This behaves like `dict.from_keys`, except:
#         - it normalises the keys to ignore case, whitespace, hypens and
#           underscores
#         - it stores the original, unnormalised key as the value of the
#           item so it can be retrieved later
#         """
#         return cls(OrderedDict([(key, key) for key in keys]))
# 
#     def keys(self):
#         return OrderedSet(super().keys())
# 
#     def __getitem__(self, key):
#         return super().__getitem__(self.make_key(key))
# 
#     def __setitem__(self, key, value):
#         super().__setitem__(self.make_key(key), value)
# 
#     def __contains__(self, key):
#         return super().__contains__(self.make_key(key))
# 
#     def get(self, key, default=None):
#         return self[key] if key in self else default
# 
#     def copy(self):
#         return self.__class__(super().copy())
# 
#     def as_dict_with_keys(self, keys):
#         return {
#             key: self.get(key) for key in keys
#         }
# 
#     @staticmethod
#     @lru_cache(maxsize=32, typed=False)
#     def make_key(original_key):
#         if original_key is None:
#             return None
#         return original_key.translate(InsensitiveDict.KEY_TRANSLATION_TABLE).lower()


# Path: notifications_utils/field.py
import re
from markupsafe import Markup
from orderedset import OrderedSet
from notifications_utils.formatters import (
    escape_html,
    strip_and_remove_obscure_whitespace,
    strip_html,
    unescaped_formatted_list,
)
from notifications_utils.insensitive_dict import InsensitiveDict

    ):
        self.content = content
        self.values = values
        self.markdown_lists = markdown_lists
        if not with_brackets:
            self.placeholder_tag = self.placeholder_tag_no_brackets
        self.sanitizer = {
            'strip': strip_html,
            'escape': escape_html,
            'passthrough': str,
        }[html]
        self.redact_missing_personalisation = redact_missing_personalisation

    def __str__(self):
        if self.values:
            return self.replaced
        return self.formatted

    def __repr__(self):
        return "{}(\"{}\", {})".format(self.__class__.__name__, self.content, self.values)  # TODO: more real

    def splitlines(self):
        return str(self).splitlines()

    @property
    def values(self):
        return self._values

    @values.setter
    def values(self, value):

        self._values = InsensitiveDict(value) if value else {}