about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/sendgrid/helpers/mail/mail.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/sendgrid/helpers/mail/mail.py')
-rw-r--r--.venv/lib/python3.12/site-packages/sendgrid/helpers/mail/mail.py1041
1 files changed, 1041 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/sendgrid/helpers/mail/mail.py b/.venv/lib/python3.12/site-packages/sendgrid/helpers/mail/mail.py
new file mode 100644
index 00000000..2472ad7d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sendgrid/helpers/mail/mail.py
@@ -0,0 +1,1041 @@
+"""Twilio SendGrid v3/mail/send response body builder"""
+from .bcc_email import Bcc
+from .cc_email import Cc
+from .content import Content
+from .custom_arg import CustomArg
+from .dynamic_template_data import DynamicTemplateData
+from .email import Email
+from .from_email import From
+from .header import Header
+from .mime_type import MimeType
+from .personalization import Personalization
+from .reply_to import ReplyTo
+from .send_at import SendAt
+from .subject import Subject
+from .substitution import Substitution
+from .template_id import TemplateId
+from .to_email import To
+
+
+class Mail(object):
+    """Creates the response body for v3/mail/send"""
+
+    def __init__(
+            self,
+            from_email=None,
+            to_emails=None,
+            subject=None,
+            plain_text_content=None,
+            html_content=None,
+            amp_html_content=None,
+            global_substitutions=None,
+            is_multiple=False):
+        """
+        Creates the response body for a v3/mail/send API call
+
+        :param from_email: The email address of the sender
+        :type from_email: From, tuple, optional
+        :param subject: The subject of the email
+        :type subject: Subject, optional
+        :param to_emails: The email address of the recipient
+        :type to_emails: To, str, tuple, list(str), list(tuple),
+                         list(To), optional
+        :param plain_text_content: The plain text body of the email
+        :type plain_text_content: string, optional
+        :param html_content: The html body of the email
+        :type html_content: string, optional
+        :param amp_html_content: The amp-html body of the email
+        :type amp_html_content: string, optional
+        """
+        self._attachments = None
+        self._categories = None
+        self._contents = None
+        self._custom_args = None
+        self._headers = None
+        self._personalizations = []
+        self._sections = None
+        self._asm = None
+        self._batch_id = None
+        self._from_email = None
+        self._ip_pool_name = None
+        self._mail_settings = None
+        self._reply_to = None
+        self._reply_to_list = None
+        self._send_at = None
+        self._subject = None
+        self._template_id = None
+        self._tracking_settings = None
+
+        # Minimum required data to send a single email
+        if from_email is not None:
+            self.from_email = from_email
+        if to_emails is not None:
+            self.add_to(to_emails, global_substitutions, is_multiple)
+        if subject is not None:
+            self.subject = subject
+        if plain_text_content is not None:
+            self.add_content(plain_text_content, MimeType.text)
+        if amp_html_content is not None:
+            self.add_content(amp_html_content, MimeType.amp)
+        if html_content is not None:
+            self.add_content(html_content, MimeType.html)
+
+    def __str__(self):
+        """A JSON-ready string representation of this Mail object.
+
+        :returns: A JSON-ready string representation of this Mail object.
+        :rtype: string
+        """
+        return str(self.get())
+
+    def _ensure_append(self, new_items, append_to, index=0):
+        """Ensure an item is appended to a list or create a new empty list
+
+        :param new_items: the item(s) to append
+        :type new_items: list(obj)
+        :param append_to: the list on which to append the items
+        :type append_to: list()
+        :param index: index of the list on which to append the items
+        :type index: int
+        """
+        append_to = append_to or []
+        append_to.insert(index, new_items)
+        return append_to
+
+    def _ensure_insert(self, new_items, insert_to):
+        """Ensure an item is inserted to a list or create a new empty list
+
+        :param new_items: the item(s) to insert
+        :type new_items: list(obj)
+        :param insert_to: the list on which to insert the items at index 0
+        :type insert_to: list()
+        """
+        insert_to = insert_to or []
+        insert_to.insert(0, new_items)
+        return insert_to
+
+    def _flatten_dicts(self, dicts):
+        """Flatten a dict
+
+        :param dicts: Flatten a dict
+        :type dicts: list(dict)
+        """
+        d = dict()
+        list_of_dicts = [d.get() for d in dicts or []]
+        return {k: v for d in list_of_dicts for k, v in d.items()}
+
+    def _get_or_none(self, from_obj):
+        """Get the JSON representation of the object, else return None
+
+        :param from_obj: Get the JSON representation of the object,
+        else return None
+        :type from_obj: obj
+        """
+        return from_obj.get() if from_obj is not None else None
+
+    def _set_emails(
+            self, emails, global_substitutions=None, is_multiple=False, p=0):
+        """Adds emails to the Personalization object
+
+        :param emails: An Email or list of Email objects
+        :type emails: Email, list(Email)
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+        # Send multiple emails to multiple recipients
+        if is_multiple is True:
+            if isinstance(emails, list):
+                for email in emails:
+                    personalization = Personalization()
+                    personalization.add_email(email)
+                    self.add_personalization(personalization)
+            else:
+                personalization = Personalization()
+                personalization.add_email(emails)
+                self.add_personalization(personalization)
+            if global_substitutions is not None:
+                if isinstance(global_substitutions, list):
+                    for substitution in global_substitutions:
+                        for p in self.personalizations:
+                            p.add_substitution(substitution)
+                else:
+                    for p in self.personalizations:
+                        p.add_substitution(global_substitutions)
+        else:
+            try:
+                personalization = self._personalizations[p]
+                has_internal_personalization = True
+            except IndexError:
+                personalization = Personalization()
+                has_internal_personalization = False
+
+            if isinstance(emails, list):
+                for email in emails:
+                    personalization.add_email(email)
+            else:
+                personalization.add_email(emails)
+
+            if global_substitutions is not None:
+                if isinstance(global_substitutions, list):
+                    for substitution in global_substitutions:
+                        personalization.add_substitution(substitution)
+                else:
+                    personalization.add_substitution(global_substitutions)
+
+            if not has_internal_personalization:
+                self.add_personalization(personalization, index=p)
+
+    @property
+    def personalizations(self):
+        """A list of one or more Personalization objects
+
+        :rtype: list(Personalization)
+        """
+        return self._personalizations
+
+    def add_personalization(self, personalization, index=0):
+        """Add a Personalization object
+
+        :param personalization: Add a Personalization object
+        :type personalization: Personalization
+        :param index: The index where to add the Personalization
+        :type index: int
+        """
+        self._personalizations = self._ensure_append(
+            personalization, self._personalizations, index)
+
+    @property
+    def to(self):
+        pass
+
+    @to.setter
+    def to(self, to_emails, global_substitutions=None, is_multiple=False, p=0):
+        """Adds To objects to the Personalization object
+
+        :param to_emails: The email addresses of all recipients
+        :type to_emails: To, str, tuple, list(str), list(tuple), list(To)
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+        if isinstance(to_emails, list):
+            for email in to_emails:
+                if isinstance(email, str):
+                    email = To(email, None)
+                if isinstance(email, tuple):
+                    email = To(email[0], email[1])
+                self.add_to(email, global_substitutions, is_multiple, p)
+        else:
+            if isinstance(to_emails, str):
+                to_emails = To(to_emails, None)
+            if isinstance(to_emails, tuple):
+                to_emails = To(to_emails[0], to_emails[1])
+            self.add_to(to_emails, global_substitutions, is_multiple, p)
+
+    def add_to(
+            self, to_email, global_substitutions=None, is_multiple=False, p=0):
+        """Adds a To object to the Personalization object
+
+        :param to_email: A To object
+        :type to_email: To, str, tuple, list(str), list(tuple), list(To)
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+
+        if isinstance(to_email, list):
+            for email in to_email:
+                if isinstance(email, str):
+                    email = To(email, None)
+                elif isinstance(email, tuple):
+                    email = To(email[0], email[1])
+                elif not isinstance(email, Email):
+                    raise ValueError(
+                        'Please use a To/Cc/Bcc, tuple, or a str for a to_email list.'
+                    )
+                self._set_emails(email, global_substitutions, is_multiple, p)
+        else:
+            if isinstance(to_email, str):
+                to_email = To(to_email, None)
+            if isinstance(to_email, tuple):
+                to_email = To(to_email[0], to_email[1])
+            if isinstance(to_email, Email):
+                p = to_email.personalization
+            self._set_emails(to_email, global_substitutions, is_multiple, p)
+
+    @property
+    def cc(self):
+        pass
+
+    @cc.setter
+    def cc(self, cc_emails, global_substitutions=None, is_multiple=False, p=0):
+        """Adds Cc objects to the Personalization object
+
+        :param cc_emails: An Cc or list of Cc objects
+        :type cc_emails: Cc, list(Cc), tuple
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+        if isinstance(cc_emails, list):
+            for email in cc_emails:
+                if isinstance(email, str):
+                    email = Cc(email, None)
+                if isinstance(email, tuple):
+                    email = Cc(email[0], email[1])
+                self.add_cc(email, global_substitutions, is_multiple, p)
+        else:
+            if isinstance(cc_emails, str):
+                cc_emails = Cc(cc_emails, None)
+            if isinstance(cc_emails, tuple):
+                cc_emails = To(cc_emails[0], cc_emails[1])
+            self.add_cc(cc_emails, global_substitutions, is_multiple, p)
+
+    def add_cc(
+            self, cc_email, global_substitutions=None, is_multiple=False, p=0):
+        """Adds a Cc object to the Personalization object
+
+        :param to_emails: An Cc object
+        :type to_emails: Cc
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+        if isinstance(cc_email, str):
+            cc_email = Cc(cc_email, None)
+        if isinstance(cc_email, tuple):
+            cc_email = Cc(cc_email[0], cc_email[1])
+        if isinstance(cc_email, Email):
+            p = cc_email.personalization
+        self._set_emails(
+            cc_email, global_substitutions, is_multiple=is_multiple, p=p)
+
+    @property
+    def bcc(self):
+        pass
+
+    @bcc.setter
+    def bcc(
+            self,
+            bcc_emails,
+            global_substitutions=None,
+            is_multiple=False,
+            p=0):
+        """Adds Bcc objects to the Personalization object
+
+        :param bcc_emails: An Bcc or list of Bcc objects
+        :type bcc_emails: Bcc, list(Bcc), tuple
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+        if isinstance(bcc_emails, list):
+            for email in bcc_emails:
+                if isinstance(email, str):
+                    email = Bcc(email, None)
+                if isinstance(email, tuple):
+                    email = Bcc(email[0], email[1])
+                self.add_bcc(email, global_substitutions, is_multiple, p)
+        else:
+            if isinstance(bcc_emails, str):
+                bcc_emails = Bcc(bcc_emails, None)
+            if isinstance(bcc_emails, tuple):
+                bcc_emails = Bcc(bcc_emails[0], bcc_emails[1])
+            self.add_bcc(bcc_emails, global_substitutions, is_multiple, p)
+
+    def add_bcc(
+            self,
+            bcc_email,
+            global_substitutions=None,
+            is_multiple=False,
+            p=0):
+        """Adds a Bcc object to the Personalization object
+
+        :param to_emails: An Bcc object
+        :type to_emails: Bcc
+        :param global_substitutions: A dict of substitutions for all recipients
+        :type global_substitutions: dict
+        :param is_multiple: Create a new personalization for each recipient
+        :type is_multiple: bool
+        :param p: p is the Personalization object or Personalization object
+                  index
+        :type p: Personalization, integer, optional
+        """
+        if isinstance(bcc_email, str):
+            bcc_email = Bcc(bcc_email, None)
+        if isinstance(bcc_email, tuple):
+            bcc_email = Bcc(bcc_email[0], bcc_email[1])
+        if isinstance(bcc_email, Email):
+            p = bcc_email.personalization
+        self._set_emails(
+            bcc_email,
+            global_substitutions,
+            is_multiple=is_multiple,
+            p=p)
+
+    @property
+    def subject(self):
+        """The global Subject object
+
+        :rtype: Subject
+        """
+        return self._subject
+
+    @subject.setter
+    def subject(self, value):
+        """The subject of the email(s)
+
+        :param value: The subject of the email(s)
+        :type value: Subject, string
+        """
+        if isinstance(value, Subject):
+            if value.personalization is not None:
+                try:
+                    personalization = \
+                        self._personalizations[value.personalization]
+                    has_internal_personalization = True
+                except IndexError:
+                    personalization = Personalization()
+                    has_internal_personalization = False
+                personalization.subject = value.subject
+
+                if not has_internal_personalization:
+                    self.add_personalization(
+                        personalization,
+                        index=value.personalization)
+            else:
+                self._subject = value
+        else:
+            self._subject = Subject(value)
+
+    @property
+    def headers(self):
+        """A list of global Header objects
+
+        :rtype: list(Header)
+        """
+        return self._headers
+
+    @property
+    def header(self):
+        pass
+
+    @header.setter
+    def header(self, headers):
+        """Add headers to the email
+
+        :param value: A list of Header objects or a dict of header key/values
+        :type value: Header, list(Header), dict
+        """
+        if isinstance(headers, list):
+            for h in headers:
+                self.add_header(h)
+        else:
+            self.add_header(headers)
+
+    def add_header(self, header):
+        """Add headers to the email globaly or to a specific Personalization
+
+        :param value: A Header object or a dict of header key/values
+        :type value: Header, dict
+        """
+        if header.personalization is not None:
+            try:
+                personalization = \
+                    self._personalizations[header.personalization]
+                has_internal_personalization = True
+            except IndexError:
+                personalization = Personalization()
+                has_internal_personalization = False
+            if isinstance(header, dict):
+                (k, v) = list(header.items())[0]
+                personalization.add_header(Header(k, v))
+            else:
+                personalization.add_header(header)
+
+            if not has_internal_personalization:
+                self.add_personalization(
+                    personalization,
+                    index=header.personalization)
+        else:
+            if isinstance(header, dict):
+                (k, v) = list(header.items())[0]
+                self._headers = self._ensure_append(
+                    Header(k, v), self._headers)
+            else:
+                self._headers = self._ensure_append(header, self._headers)
+
+    @property
+    def substitution(self):
+        pass
+
+    @substitution.setter
+    def substitution(self, substitution):
+        """Add substitutions to the email
+
+        :param value: Add substitutions to the email
+        :type value: Substitution, list(Substitution)
+        """
+        if isinstance(substitution, list):
+            for s in substitution:
+                self.add_substitution(s)
+        else:
+            self.add_substitution(substitution)
+
+    def add_substitution(self, substitution):
+        """Add a substitution to the email
+
+        :param value: Add a substitution to the email
+        :type value: Substitution
+        """
+        if substitution.personalization:
+            try:
+                personalization = \
+                    self._personalizations[substitution.personalization]
+                has_internal_personalization = True
+            except IndexError:
+                personalization = Personalization()
+                has_internal_personalization = False
+            personalization.add_substitution(substitution)
+
+            if not has_internal_personalization:
+                self.add_personalization(
+                    personalization, index=substitution.personalization)
+        else:
+            if isinstance(substitution, list):
+                for s in substitution:
+                    for p in self.personalizations:
+                        p.add_substitution(s)
+            else:
+                for p in self.personalizations:
+                    p.add_substitution(substitution)
+
+    @property
+    def custom_args(self):
+        """A list of global CustomArg objects
+
+        :rtype: list(CustomArg)
+        """
+        return self._custom_args
+
+    @property
+    def custom_arg(self):
+        return self._custom_args
+
+    @custom_arg.setter
+    def custom_arg(self, custom_arg):
+        """Add custom args to the email
+
+        :param value: A list of CustomArg objects or a dict of custom arg
+                      key/values
+        :type value: CustomArg, list(CustomArg), dict
+        """
+        if isinstance(custom_arg, list):
+            for c in custom_arg:
+                self.add_custom_arg(c)
+        else:
+            self.add_custom_arg(custom_arg)
+
+    def add_custom_arg(self, custom_arg):
+        """Add custom args to the email globaly or to a specific Personalization
+
+        :param value: A CustomArg object or a dict of custom arg key/values
+        :type value: CustomArg, dict
+        """
+        if custom_arg.personalization is not None:
+            try:
+                personalization = \
+                    self._personalizations[custom_arg.personalization]
+                has_internal_personalization = True
+            except IndexError:
+                personalization = Personalization()
+                has_internal_personalization = False
+            if isinstance(custom_arg, dict):
+                (k, v) = list(custom_arg.items())[0]
+                personalization.add_custom_arg(CustomArg(k, v))
+            else:
+                personalization.add_custom_arg(custom_arg)
+
+            if not has_internal_personalization:
+                self.add_personalization(
+                    personalization, index=custom_arg.personalization)
+        else:
+            if isinstance(custom_arg, dict):
+                (k, v) = list(custom_arg.items())[0]
+                self._custom_args = self._ensure_append(
+                    CustomArg(k, v), self._custom_args)
+            else:
+                self._custom_args = self._ensure_append(
+                    custom_arg, self._custom_args)
+
+    @property
+    def send_at(self):
+        """The global SendAt object
+
+        :rtype: SendAt
+        """
+        return self._send_at
+
+    @send_at.setter
+    def send_at(self, value):
+        """A unix timestamp specifying when your email should
+        be delivered.
+
+        :param value: A unix timestamp specifying when your email should
+        be delivered.
+        :type value: SendAt, int
+        """
+        if isinstance(value, SendAt):
+            if value.personalization is not None:
+                try:
+                    personalization = \
+                        self._personalizations[value.personalization]
+                    has_internal_personalization = True
+                except IndexError:
+                    personalization = Personalization()
+                    has_internal_personalization = False
+                personalization.send_at = value.send_at
+
+                if not has_internal_personalization:
+                    self.add_personalization(
+                        personalization, index=value.personalization)
+            else:
+                self._send_at = value
+        else:
+            self._send_at = SendAt(value)
+
+    @property
+    def dynamic_template_data(self):
+        pass
+
+    @dynamic_template_data.setter
+    def dynamic_template_data(self, value):
+        """Data for a transactional template
+
+        :param value: Data for a transactional template
+        :type value: DynamicTemplateData, a JSON-serializable structure
+        """
+        if not isinstance(value, DynamicTemplateData):
+            value = DynamicTemplateData(value)
+        try:
+            personalization = self._personalizations[value.personalization]
+            has_internal_personalization = True
+        except IndexError:
+            personalization = Personalization()
+            has_internal_personalization = False
+        personalization.dynamic_template_data = value.dynamic_template_data
+
+        if not has_internal_personalization:
+            self.add_personalization(
+                personalization, index=value.personalization)
+
+    @property
+    def from_email(self):
+        """The email address of the sender
+
+        :rtype: From
+        """
+        return self._from_email
+
+    @from_email.setter
+    def from_email(self, value):
+        """The email address of the sender
+
+        :param value: The email address of the sender
+        :type value: From, str, tuple
+        """
+        if isinstance(value, str):
+            value = From(value, None)
+        if isinstance(value, tuple):
+            value = From(value[0], value[1])
+        self._from_email = value
+
+    @property
+    def reply_to(self):
+        """The reply to email address
+
+        :rtype: ReplyTo
+        """
+        return self._reply_to
+
+    @reply_to.setter
+    def reply_to(self, value):
+        """The reply to email address
+
+        :param value: The reply to email address
+        :type value: ReplyTo, str, tuple
+        """
+        if isinstance(value, str):
+            value = ReplyTo(value, None)
+        if isinstance(value, tuple):
+            value = ReplyTo(value[0], value[1])
+        self._reply_to = value
+
+    @property
+    def reply_to_list(self):
+        """A list of ReplyTo email addresses
+
+        :rtype: list(ReplyTo), tuple
+        """
+        return self._reply_to_list
+
+    @reply_to_list.setter
+    def reply_to_list(self, value):
+        """A list of ReplyTo email addresses
+
+        :param value: A list of ReplyTo email addresses
+        :type value: list(ReplyTo), tuple
+        """
+        if isinstance(value, list):
+            for reply in value:
+                if isinstance(reply, ReplyTo):
+                    if not isinstance(reply.email, str):
+                        raise ValueError('You must provide an email for each entry in a reply_to_list')
+                else:
+                    raise ValueError(
+                        'Please use a list of ReplyTos for a reply_to_list.'
+                    )
+            self._reply_to_list = value
+
+    @property
+    def contents(self):
+        """The contents of the email
+
+        :rtype: list(Content)
+        """
+        return self._contents
+
+    @property
+    def content(self):
+        pass
+
+    @content.setter
+    def content(self, contents):
+        """The content(s) of the email
+
+        :param contents: The content(s) of the email
+        :type contents: Content, list(Content)
+        """
+        if isinstance(contents, list):
+            for c in contents:
+                self.add_content(c)
+        else:
+            self.add_content(contents)
+
+    def add_content(self, content, mime_type=None):
+        """Add content to the email
+
+        :param contents: Content to be added to the email
+        :type contents: Content
+        :param mime_type: Override the mime type
+        :type mime_type: MimeType, str
+        """
+        if isinstance(content, str):
+            content = Content(mime_type, content)
+        # Content of mime type text/plain must always come first, followed by text/x-amp-html and then text/html
+        if content.mime_type == MimeType.text:
+            self._contents = self._ensure_insert(content, self._contents)
+        elif content.mime_type == MimeType.amp:
+            if self._contents:
+                for _content in self._contents:
+                    # this is written in the context that plain text content will always come earlier than the html content
+                    if _content.mime_type == MimeType.text:
+                        index = 1
+                        break
+                    elif _content.mime_type == MimeType.html:
+                        index = 0
+                        break
+            else:
+                index = 0
+            self._contents = self._ensure_append(
+                content, self._contents, index=index)
+        else:
+            if self._contents:
+                index = len(self._contents)
+            else:
+                index = 0
+            self._contents = self._ensure_append(
+                content, self._contents, index=index)
+
+    @property
+    def attachments(self):
+        """The attachments to this email
+
+        :rtype: list(Attachment)
+        """
+        return self._attachments
+
+    @property
+    def attachment(self):
+        pass
+
+    @attachment.setter
+    def attachment(self, attachment):
+        """Add attachment(s) to this email
+
+        :param attachment: Add attachment(s) to this email
+        :type attachment: Attachment, list(Attachment)
+        """
+        if isinstance(attachment, list):
+            for a in attachment:
+                self.add_attachment(a)
+        else:
+            self.add_attachment(attachment)
+
+    def add_attachment(self, attachment):
+        """Add an attachment to this email
+
+        :param attachment: Add an attachment to this email
+        :type attachment: Attachment
+        """
+        self._attachments = self._ensure_append(attachment, self._attachments)
+
+    @property
+    def template_id(self):
+        """The transactional template id for this email
+
+        :rtype: TemplateId
+        """
+        return self._template_id
+
+    @template_id.setter
+    def template_id(self, value):
+        """The transactional template id for this email
+
+        :param value: The transactional template id for this email
+        :type value: TemplateId
+        """
+        if isinstance(value, TemplateId):
+            self._template_id = value
+        else:
+            self._template_id = TemplateId(value)
+
+    @property
+    def sections(self):
+        """The block sections of code to be used as substitutions
+
+        :rtype: Section
+        """
+        return self._sections
+
+    @property
+    def section(self):
+        pass
+
+    @section.setter
+    def section(self, section):
+        """The block sections of code to be used as substitutions
+
+        :rtype: Section, list(Section)
+        """
+        if isinstance(section, list):
+            for h in section:
+                self.add_section(h)
+        else:
+            self.add_section(section)
+
+    def add_section(self, section):
+        """A block section of code to be used as substitutions
+
+        :param section: A block section of code to be used as substitutions
+        :type section: Section
+        """
+        self._sections = self._ensure_append(section, self._sections)
+
+    @property
+    def categories(self):
+        """The categories assigned to this message
+
+        :rtype: list(Category)
+        """
+        return self._categories
+
+    @property
+    def category(self):
+        pass
+
+    @category.setter
+    def category(self, categories):
+        """Add categories assigned to this message
+
+        :rtype: list(Category)
+        """
+        if isinstance(categories, list):
+            for c in categories:
+                self.add_category(c)
+        else:
+            self.add_category(categories)
+
+    def add_category(self, category):
+        """Add a category assigned to this message
+
+        :rtype: Category
+        """
+        self._categories = self._ensure_append(category, self._categories)
+
+    @property
+    def batch_id(self):
+        """The batch id for this email
+
+        :rtype: BatchId
+        """
+        return self._batch_id
+
+    @batch_id.setter
+    def batch_id(self, value):
+        """The batch id for this email
+
+        :param value: The batch id for this email
+        :type value: BatchId
+        """
+        self._batch_id = value
+
+    @property
+    def asm(self):
+        """An object specifying unsubscribe behavior.
+
+        :rtype: Asm
+        """
+        return self._asm
+
+    @asm.setter
+    def asm(self, value):
+        """An object specifying unsubscribe behavior.
+
+        :param value: An object specifying unsubscribe behavior.
+        :type value: Asm
+        """
+        self._asm = value
+
+    @property
+    def ip_pool_name(self):
+        """The IP Pool that you would like to send this email from
+
+        :rtype: IpPoolName
+        """
+        return self._ip_pool_name
+
+    @ip_pool_name.setter
+    def ip_pool_name(self, value):
+        """The IP Pool that you would like to send this email from
+
+        :paran value: The IP Pool that you would like to send this email from
+        :type value: IpPoolName
+        """
+        self._ip_pool_name = value
+
+    @property
+    def mail_settings(self):
+        """The mail settings for this email
+
+        :rtype: MailSettings
+        """
+        return self._mail_settings
+
+    @mail_settings.setter
+    def mail_settings(self, value):
+        """The mail settings for this email
+
+        :param value: The mail settings for this email
+        :type value: MailSettings
+        """
+        self._mail_settings = value
+
+    @property
+    def tracking_settings(self):
+        """The tracking settings for this email
+
+        :rtype: TrackingSettings
+        """
+        return self._tracking_settings
+
+    @tracking_settings.setter
+    def tracking_settings(self, value):
+        """The tracking settings for this email
+
+        :param value: The tracking settings for this email
+        :type value: TrackingSettings
+        """
+        self._tracking_settings = value
+
+    def get(self):
+        """
+        Get a JSON-ready representation of this Mail object.
+
+        :returns: This Mail object, ready for use in a request body.
+        :rtype: dict
+        """
+        mail = {
+            'from': self._get_or_none(self.from_email),
+            'subject': self._get_or_none(self.subject),
+            'personalizations': [p.get() for p in self.personalizations or []],
+            'content': [c.get() for c in self.contents or []],
+            'attachments': [a.get() for a in self.attachments or []],
+            'template_id': self._get_or_none(self.template_id),
+            'sections': self._flatten_dicts(self.sections),
+            'headers': self._flatten_dicts(self.headers),
+            'categories': [c.get() for c in self.categories or []],
+            'custom_args': self._flatten_dicts(self.custom_args),
+            'send_at': self._get_or_none(self.send_at),
+            'batch_id': self._get_or_none(self.batch_id),
+            'asm': self._get_or_none(self.asm),
+            'ip_pool_name': self._get_or_none(self.ip_pool_name),
+            'mail_settings': self._get_or_none(self.mail_settings),
+            'tracking_settings': self._get_or_none(self.tracking_settings),
+            'reply_to': self._get_or_none(self.reply_to),
+            'reply_to_list': [r.get() for r in self.reply_to_list or []],
+        }
+
+        return {key: value for key, value in mail.items()
+                if value is not None and value != [] and value != {}}
+
+    @classmethod
+    def from_EmailMessage(cls, message):
+        """Create a Mail object from an instance of
+        email.message.EmailMessage.
+
+        :type message: email.message.EmailMessage
+        :rtype: Mail
+        """
+        mail = cls(
+            from_email=Email(message.get('From')),
+            subject=message.get('Subject'),
+            to_emails=Email(message.get('To')),
+        )
+        try:
+            body = message.get_content()
+        except AttributeError:
+            # Python2
+            body = message.get_payload()
+        mail.add_content(Content(
+            message.get_content_type(),
+            body.strip()
+        ))
+        for k, v in message.items():
+            mail.add_header(Header(k, v))
+        return mail