# Path: notifications_utils/field.py
# class Field:
# 
#     """
#     An instance of Field represents a string of text which may contain
#     placeholders.
# 
#     If values are provided the field replaces the placeholders with the
#     corresponding values. If a value for a placeholder is missing then
#     the field will highlight the placeholder by wrapping it in some HTML.
# 
#     A template can have several fields, for example an email template
#     has a field for the body and a field for the subject.
#     """
# 
#     placeholder_pattern = re.compile(
#         r'\({2}'        # opening ((
#         r'([^()]+)'     # body of placeholder - potentially standard or conditional.
#         r'\){2}'        # closing ))
#     )
#     placeholder_tag = "<span class='placeholder'>(({}))</span>"
#     conditional_placeholder_tag = "<span class='placeholder-conditional'>(({}??</span>{}))"
#     placeholder_tag_no_brackets = "<span class='placeholder-no-brackets'>{}</span>"
#     placeholder_tag_redacted = "<span class='placeholder-redacted'>hidden</span>"
# 
#     def __init__(
#         self,
#         content,
#         values=None,
#         with_brackets=True,
#         html='strip',
#         markdown_lists=False,
#         redact_missing_personalisation=False,
#     ):
#         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 {}
# 
#     def format_match(self, match):
#         placeholder = Placeholder.from_match(match)
# 
#         if self.redact_missing_personalisation:
#             return self.placeholder_tag_redacted
# 
#         if placeholder.is_conditional():
#             return self.conditional_placeholder_tag.format(
#                 placeholder.name,
#                 placeholder.conditional_text
#             )
# 
#         return self.placeholder_tag.format(
#             placeholder.name
#         )
# 
#     def replace_match(self, match):
#         placeholder = Placeholder.from_match(match)
#         replacement = self.values.get(placeholder.name)
# 
#         if placeholder.is_conditional() and replacement is not None:
#             return placeholder.get_conditional_body(replacement)
# 
#         replaced_value = self.get_replacement(placeholder)
#         if replaced_value is not None:
#             return self.get_replacement(placeholder)
# 
#         return self.format_match(match)
# 
#     def get_replacement(self, placeholder):
#         replacement = self.values.get(placeholder.name)
#         if replacement is None:
#             return None
# 
#         if isinstance(replacement, list):
#             vals = (
#                 strip_and_remove_obscure_whitespace(str(val))
#                 for val in replacement if val is not None
#             )
#             vals = list(filter(None, vals))
#             if not vals:
#                 return ''
#             return self.sanitizer(self.get_replacement_as_list(vals))
# 
#         return self.sanitizer(str(replacement))
# 
#     def get_replacement_as_list(self, replacement):
#         if self.markdown_lists:
#             return '\n\n' + '\n'.join(
#                 '* {}'.format(item) for item in replacement
#             )
#         return unescaped_formatted_list(replacement, before_each='', after_each='')
# 
#     @property
#     def _raw_formatted(self):
#         return re.sub(
#             self.placeholder_pattern, self.format_match, self.sanitizer(self.content)
#         )
# 
#     @property
#     def formatted(self):
#         return Markup(self._raw_formatted)
# 
#     @property
#     def placeholders(self):
#         if not getattr(self, 'content', ''):
#             return set()
#         return OrderedSet(
#             Placeholder(body).name for body in re.findall(
#                 self.placeholder_pattern, self.content
#             )
#         )
# 
#     @property
#     def replaced(self):
#         return re.sub(
#             self.placeholder_pattern, self.replace_match, self.sanitizer(self.content)
#         )
# 
# def str2bool(value):
#     if not value:
#         return False
#     return str(value).lower() in ("yes", "y", "true", "t", "1", "include", "show")


import pytest
from notifications_utils.field import Field, str2bool
            "((name)) ((colour))",
            {'name': 'Jo', 'colour': None},
            "Jo <span class='placeholder'>((colour))</span>",
        ),
        (
            "((show_thing??thing)) ((colour))",
            {'colour': 'red'},
            "<span class='placeholder-conditional'>((show_thing??</span>thing)) red",
        ),
    ]
)
def test_handling_of_missing_values(content, values, expected):
    assert str(Field(content, values)) == expected


@pytest.mark.parametrize(
    "value", [
        '0',
        0, 2, 99.99999,
        'off',
        'exclude',
        'no'
        'any random string',
        'false',
        False,
        [], {}, (),
        ['true'], {'True': True}, (True, 'true', 1)
    ]
)
def test_what_will_not_trigger_conditional_placeholder(value):

# Path: notifications_utils/field.py
# class Field:
# 
#     """
#     An instance of Field represents a string of text which may contain
#     placeholders.
# 
#     If values are provided the field replaces the placeholders with the
#     corresponding values. If a value for a placeholder is missing then
#     the field will highlight the placeholder by wrapping it in some HTML.
# 
#     A template can have several fields, for example an email template
#     has a field for the body and a field for the subject.
#     """
# 
#     placeholder_pattern = re.compile(
#         r'\({2}'        # opening ((
#         r'([^()]+)'     # body of placeholder - potentially standard or conditional.
#         r'\){2}'        # closing ))
#     )
#     placeholder_tag = "<span class='placeholder'>(({}))</span>"
#     conditional_placeholder_tag = "<span class='placeholder-conditional'>(({}??</span>{}))"
#     placeholder_tag_no_brackets = "<span class='placeholder-no-brackets'>{}</span>"
#     placeholder_tag_redacted = "<span class='placeholder-redacted'>hidden</span>"
# 
#     def __init__(
#         self,
#         content,
#         values=None,
#         with_brackets=True,
#         html='strip',
#         markdown_lists=False,
#         redact_missing_personalisation=False,
#     ):
#         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 {}
# 
#     def format_match(self, match):
#         placeholder = Placeholder.from_match(match)
# 
#         if self.redact_missing_personalisation:
#             return self.placeholder_tag_redacted
# 
#         if placeholder.is_conditional():
#             return self.conditional_placeholder_tag.format(
#                 placeholder.name,
#                 placeholder.conditional_text
#             )
# 
#         return self.placeholder_tag.format(
#             placeholder.name
#         )
# 
#     def replace_match(self, match):
#         placeholder = Placeholder.from_match(match)
#         replacement = self.values.get(placeholder.name)
# 
#         if placeholder.is_conditional() and replacement is not None:
#             return placeholder.get_conditional_body(replacement)
# 
#         replaced_value = self.get_replacement(placeholder)
#         if replaced_value is not None:
#             return self.get_replacement(placeholder)
# 
#         return self.format_match(match)
# 
#     def get_replacement(self, placeholder):
#         replacement = self.values.get(placeholder.name)
#         if replacement is None:
#             return None
# 
#         if isinstance(replacement, list):
#             vals = (
#                 strip_and_remove_obscure_whitespace(str(val))
#                 for val in replacement if val is not None
#             )
#             vals = list(filter(None, vals))
#             if not vals:
#                 return ''
#             return self.sanitizer(self.get_replacement_as_list(vals))
# 
#         return self.sanitizer(str(replacement))
# 
#     def get_replacement_as_list(self, replacement):
#         if self.markdown_lists:
#             return '\n\n' + '\n'.join(
#                 '* {}'.format(item) for item in replacement
#             )
#         return unescaped_formatted_list(replacement, before_each='', after_each='')
# 
#     @property
#     def _raw_formatted(self):
#         return re.sub(
#             self.placeholder_pattern, self.format_match, self.sanitizer(self.content)
#         )
# 
#     @property
#     def formatted(self):
#         return Markup(self._raw_formatted)
# 
#     @property
#     def placeholders(self):
#         if not getattr(self, 'content', ''):
#             return set()
#         return OrderedSet(
#             Placeholder(body).name for body in re.findall(
#                 self.placeholder_pattern, self.content
#             )
#         )
# 
#     @property
#     def replaced(self):
#         return re.sub(
#             self.placeholder_pattern, self.replace_match, self.sanitizer(self.content)
#         )
# 
# def str2bool(value):
#     if not value:
#         return False
#     return str(value).lower() in ("yes", "y", "true", "t", "1", "include", "show")


# Path: tests/test_field.py
import pytest
from notifications_utils.field import Field, str2bool

            "((name)) ((colour))",
            {'name': 'Jo', 'colour': None},
            "Jo <span class='placeholder'>((colour))</span>",
        ),
        (
            "((show_thing??thing)) ((colour))",
            {'colour': 'red'},
            "<span class='placeholder-conditional'>((show_thing??</span>thing)) red",
        ),
    ]
)
def test_handling_of_missing_values(content, values, expected):
    assert str(Field(content, values)) == expected


@pytest.mark.parametrize(
    "value", [
        '0',
        0, 2, 99.99999,
        'off',
        'exclude',
        'no'
        'any random string',
        'false',
        False,
        [], {}, (),
        ['true'], {'True': True}, (True, 'true', 1)
    ]
)
def test_what_will_not_trigger_conditional_placeholder(value):

    assert str2bool(value) is False