from datetime import datetime


class BaseType(object):
    """
    Base class for all calculated fields
    """
    _base_name = None

    def __init__(self, name='undef'):
        """
        Create a new custom field
        :param name: name of field
        """
        self._base_name = name

    @property
    def name(self):
        """
        :return: the name of the field (string)
        """
        return self._base_name

    def from_internal(self, doc):
        """
        Return the field in 'external' format
        :param doc: the current record
        :return: the field in external format
        """
        return doc.get(self._base_name, None)

    def to_internal(self, doc, value):
        """
        Return the field in internal format
        :param doc: the document we're dealing with
        :param value: the field in external format
        :return: the field in internal format
        """
        doc[self._base_name] = value


class DateType(BaseType):

    _format = None

    def __init__(self, name, fmt='%d/%m/%Y'):
        """
        Set the date format
        :param fmt: valid date format string
        """
        super().__init__(name)
        self._format = fmt

    def from_internal(self, doc):
        """
        Convert from an integer (internal) to a string (external)
        :param doc: current record
        :return: date (string)
        """
        val = doc.get(self._base_name, None)
        return datetime.fromtimestamp(val).strftime(self._format)

    def to_internal(self, doc, value):
        """
        Convert from external string representation to internal int format
        :param doc: current record
        :param value: string date
        :return: integer date
        """
        doc[self._base_name] = datetime.strptime(value, self._format).timestamp()


class AgeType(BaseType):

    def from_internal(self, doc):
        """
        Calculate age based on current time and 'dob' field
        :param doc: the current record
        :return: age (integer)
        """
        now = datetime.now()
        val = doc.get(self._base_name, now)
        dob = datetime.fromtimestamp(val)
        return (now - dob).days // 365


class NameType(BaseType):

    def from_internal(self, doc):
        """
        Return a 'full name' as a concatenation of fields
        :param doc: the current record
        :return: name (string)
        """
        name = ''
        for field in self._base_name:
            if len(name):
                name += ' '
            name += doc.get(field, '')
        return name

from datetime import datetime


class BaseType(object):
    """
    Base class for all calculated fields
    """
    _base_name = None

    def __init__(self, name='undef'):
        """
        Create a new custom field
        :param name: name of field
        """
        self._base_name = name

    @property
    def name(self):
        """
        :return: the name of the field (string)
        """
        return self._base_name

    def from_internal(self, doc):
        """
        Return the field in 'external' format
        :param doc: the current record
        :return: the field in external format
        """
        return doc.get(self._base_name, None)

    def to_internal(self, doc, value):
        """
        Return the field in internal format
        :param doc: the document we're dealing with
        :param value: the field in external format
        :return: the field in internal format
        """
        doc[self._base_name] = value


class DateType(BaseType):

    _format = None

    def __init__(self, name, fmt='%d/%m/%Y'):
        """
        Set the date format
        :param fmt: valid date format string
        """
        super().__init__(name)
        self._format = fmt

    def from_internal(self, doc):
        """
        Convert from an integer (internal) to a string (external)
        :param doc: current record
        :return: date (string)
        """
        val = doc.get(self._base_name, None)
        return datetime.fromtimestamp(val).strftime(self._format)

    def to_internal(self, doc, value):
        """
        Convert from external string representation to internal int format
        :param doc: current record
        :param value: string date
        :return: integer date
        """
        doc[self._base_name] = datetime.strptime(value, self._format).timestamp()


class AgeType(BaseType):

    def from_internal(self, doc):
        """
        Calculate age based on current time and 'dob' field
        :param doc: the current record
        :return: age (integer)
        """
        now = datetime.now()
        val = doc.get(self._base_name, now)
        dob = datetime.fromtimestamp(val)
        return (now - dob).days // 365


class NameType(BaseType):

    def from_internal(self, doc):
        """
        Return a 'full name' as a concatenation of fields
        :param doc: the current record
        :return: name (string)
        """
        name = ''
        for field in self._base_name:
            if len(name):
                name += ' '
            name += doc.get(field, '')
        return name

Base class for all calculated fields Create a new custom field         :param name: name of field :return: the name of the field (string) Return the field in 'external' format         :param doc: the current record         :return: the field in external format Return the field in internal format         :param doc: the document we're dealing with         :param value: the field in external format         :return: the field in internal format Set the date format         :param fmt: valid date format string Convert from an integer (internal) to a string (external)         :param doc: current record         :return: date (string) Convert from external string representation to internal int format         :param doc: current record         :param value: string date         :return: integer date Calculate age based on current time and 'dob' field         :param doc: the current record         :return: age (integer) Return a 'full name' as a concatenation of fields         :param doc: the current record         :return: name (string)