Skip to content

Mail

mail

Functionality around mailing

ToDo

  • add logging where appropriate
  • Find out why extra=Extra.allow causes mypy to fail. Seems like a bug in pydantic.
  • Sending mails is quite slow, so using tqdm to show feedback to the current progress would be nice

Mail

Mail template

Use the data field to store additional information

You can use the typical Format String Syntax and the objects recipient and mail to access metadata to complement the template, e.g.:

Hello {recipient.address_as},

We hope it's ok to address you your first name rather than using your full name being {recipient.name}.
Have you read the email's subject '{mail.subject}'? How is your work right now at {recipient.data.company}?

Cheers!

agent_id: str instance-attribute

data: MetaData | None = None class-attribute instance-attribute

recipients: list[Recipient] instance-attribute

status: str = 'solved' class-attribute instance-attribute

subject: str instance-attribute

team_id: str instance-attribute

text: str instance-attribute

MailClient(helpdesk_client: HelpDeskClient | None = None)

Mail client for mass mails over HelpDesk

Source code in src/pytanis/helpdesk/mail.py
def __init__(self, helpdesk_client: HelpDeskClient | None = None):
    if helpdesk_client is None:
        helpdesk_client = HelpDeskClient()
    self._helpdesk_client = helpdesk_client
    self.dry_run: Callable[[NewTicket], None] = self.print_new_ticket

batch_size: int = 10 class-attribute instance-attribute

dry_run: Callable[[NewTicket], None] = self.print_new_ticket instance-attribute

wait_time: int = 20 class-attribute instance-attribute

print_new_ticket(ticket: NewTicket) staticmethod

Default action in a dry-run. Mainly for making sure you sent what you mean!

Overwrite it by assigning to self.dry_run another function

ToDo: Make this function nice, maybe use the rich library even

Source code in src/pytanis/helpdesk/mail.py
@staticmethod
def print_new_ticket(ticket: NewTicket):
    """Default action in a dry-run. Mainly for making sure you sent what you mean!

    Overwrite it by assigning to self.dry_run another function

    ToDo: Make this function nice, maybe use the `rich` library even
    """
    print('#' * 40)  # noqa: T201
    print(f'Recipient: {ticket.requester.name} <{ticket.requester.email}>')  # noqa: T201
    print(f'Subject: {ticket.subject}')  # noqa: T201
    print(f'{ticket.message.text}')  # noqa: T201

send(mail: Mail, *, dry_run: bool = True) -> tuple[list[tuple[Recipient, Ticket | None]], list[tuple[Recipient, Exception]]]

Send a mail to all recipients using HelpDesk

Source code in src/pytanis/helpdesk/mail.py
def send(
    self, mail: Mail, *, dry_run: bool = True
) -> tuple[list[tuple[Recipient, Ticket | None]], list[tuple[Recipient, Exception]]]:
    """Send a mail to all recipients using HelpDesk"""
    errors = []
    tickets = []
    for idx, recipient in enumerate(tqdm(mail.recipients), start=1):
        recip_mail = mail.model_copy()
        try:
            recip_mail.subject = mail.subject.format(recipient=recipient, mail=mail)
            # be aware here that the body might reference to subject line, so it must be filled already
            recip_mail.text = recip_mail.text.format(recipient=recipient, mail=recip_mail)
            ticket = self._create_ticket(recip_mail, recipient)
            if dry_run:
                self.print_new_ticket(ticket)
                resp_ticket = None
            else:
                resp = self._helpdesk_client.create_ticket(ticket)
                resp_ticket = Ticket.model_validate(resp)
        except Exception as e:
            errors.append((recipient, e))
        else:
            tickets.append((recipient, resp_ticket))
        if (idx % self.batch_size == 0) and not dry_run:
            time.sleep(self.wait_time)

    return tickets, errors

MetaData

Additional, arbitrary metadata provided by the user like for template filling

model_config = ConfigDict(extra='allow') class-attribute instance-attribute

Recipient

Details about the recipient

Use the data field to store additional information

address_as: str | None = None class-attribute instance-attribute

data: MetaData | None = None class-attribute instance-attribute

email: str instance-attribute

name: str instance-attribute

fill_with_name(v, values) classmethod

Source code in src/pytanis/helpdesk/mail.py
@validator('address_as')
@classmethod
def fill_with_name(cls, v, values):
    if v is None:
        v = values['name']
    return v