"""
MOTECH modules can update the `serializers` dictionary to register
their own serializer functions.

They would do something like the following::

    from corehq.motech.serializers import serializers
    serializers.update({
        (from_data_type, to_data_type): serializer_function,
    })

Serializer functions accept a value in `from_data_type`, and return a
value in `to_data_type`.

"""
import datetime
import re
from typing import Any

from dateutil import parser as dateutil_parser

from corehq.motech.const import (
    COMMCARE_DATA_TYPE_BOOLEAN,
    COMMCARE_DATA_TYPE_DECIMAL,
    COMMCARE_DATA_TYPE_INTEGER,
    COMMCARE_DATA_TYPE_TEXT,
)

FALSEY_STRINGS = ('0', 'false', 'no')


def to_boolean(value: Any) -> bool:
    """
    Converts truthy and falsey values, including falsey strings, to
    boolean.

    >>> to_boolean(1)
    True
    >>> to_boolean('No')
    False

    """
    if isinstance(value, str) and value.lower() in FALSEY_STRINGS:
        return False
    return bool(value)


def to_decimal(value):
    try:
        return float(value)
    except ValueError:
        return None


def to_integer(value):
    try:
        return int(value)
    except (TypeError, ValueError):
        return None


def to_text(value):
    if value is None:
        return ''
    if not isinstance(value, str):
        return str(value)
    return value


def to_date_str(value):
    """
    Drop the time and timezone to export date-only values

    >>> to_date_str('2017-06-27T12:00:00+0530')
    '2017-06-27'

    """
    if isinstance(value, str):
        if not re.match(r'\d{4}-\d{2}-\d{2}', value):
            raise ValueError('"{}" is not recognised as a date or a datetime'.format(value))
        value = dateutil_parser.parse(value)
    if isinstance(value, (datetime.date, datetime.datetime)):
        return value.strftime('%Y-%m-%d')


serializers = {
    # (from_data_type, to_data_type): function
    (None, COMMCARE_DATA_TYPE_BOOLEAN): to_boolean,
    (None, COMMCARE_DATA_TYPE_DECIMAL): to_decimal,
    (None, COMMCARE_DATA_TYPE_INTEGER): to_integer,
    (None, COMMCARE_DATA_TYPE_TEXT): to_text,
}

@@ -489,16 +489,64 @@
 parser%0A%0A
+from dimagi.utils.parsing import FALSE_STRINGS%0A%0A
 from cor
@@ -703,47 +703,8 @@
 %0A)%0A%0A
-FALSEY_STRINGS = ('0', 'false', 'no')%0A%0A
 %0Adef
@@ -957,17 +957,16 @@
 in FALSE
-Y
 _STRINGS

Use existing constant