mails.py 4.17 KB
Newer Older
1 2
import logging
import mailbox
scmalte's avatar
scmalte committed
3
import email
4
import argparse
scmalte's avatar
scmalte committed
5 6
from email.message import EmailMessage
from email.headerregistry import Address
7
# import quopri
8
import os
9 10 11
import pathlib
import glob
import csv
scmalte's avatar
scmalte committed
12
import datetime
13
import re
14 15
from .utils import logging as logutils

16 17 18
DEFAULT_CLUSTER_FILES_DIR="_clusters"
DEFAULT_CLUSTER_STUDENTS_CSV_FILE_GLOB="cluster-students-[0-9]*.csv"
DEFAULT_MAIL_BODY_FILE="./_static/cluster_mail.txt"
19 20
DEFAULT_STUDENTS_RECEIVE_MAILS_AS_HEADER="To" # To, CC, BCC

21 22 23 24 25 26 27 28
def create_emails(
    cluster_files_dir,
    cluster_students_csv_file_glob,
    mail_body_file,
    students_receive_mails_as_header,
    sender_address,
    subject,
    mbox_filename):
29

30 31
  logutils.configure_level_and_format()

32 33
  mbox_path = pathlib.Path(mbox_filename)
  mbox_msf_path = pathlib.Path(mbox_filename + ".msf")
34

scmalte's avatar
scmalte committed
35
  logging.warn("ATTENTION: If you continue, the files")
36 37 38
  logging.warn("  {}".format(mbox_path))
  logging.warn("  {}".format(mbox_msf_path))
  logging.warn("will be overwritten, if they exist. Make sure that do not have any pending unsent messages!")
39 40 41 42 43 44 45 46 47
  
  if (input("Do you want to continue (enter 'yes')? ").lower() != "yes"):
    logging.debug("Exiting program because user did not confirm to continue")
    exit(0)

  # Delete mailbox file and corresponding summary/index file
  mbox_path.unlink(missing_ok=True)
  mbox_msf_path.unlink(missing_ok=True)

scmalte's avatar
scmalte committed
48 49 50
  logging.info("Reading mail body from file {}".format(mail_body_file))
  logging.info("IMPORTANT: File encoding must be UTF-8!")
  with open(mail_body_file, encoding="utf-8") as body_fh:
51 52 53 54 55
    mail_body = body_fh.read()

  mbox = mailbox.mbox(mbox_path)
  mbox.lock()
  try:
56 57 58
    cluster_students_glob = os.path.join(cluster_files_dir, cluster_students_csv_file_glob)
    logging.info("Taking students per cluster from files {}".format(cluster_students_glob))

59 60 61 62
    cluster_csv_files = glob.glob(cluster_students_glob)
    logging.info("Found {} matching files".format(len(cluster_csv_files)))

    for students_per_cluster_csv in cluster_csv_files:
63 64 65
      with open(students_per_cluster_csv, newline='') as csv_fh:
        cluster_csv = list(csv.DictReader(csv_fh))

scmalte's avatar
scmalte committed
66
      recipients = [Address(addr_spec=row["Email"]) for row in cluster_csv]
67

scmalte's avatar
scmalte committed
68 69 70 71 72 73 74 75
      # test_addresses = ["foo@bar.com"]
      # recipients = [Address(addr_spec=addr) for adrr in test_addresses]
      
      logging.debug("Creating mail to {}".format(", ".join([str(r) for r in recipients])))

      msg = EmailMessage()
      msg["From"] = sender_address
      msg["To"] = recipients
76
      msg["Subject"] = subject
scmalte's avatar
scmalte committed
77 78 79
      msg['Date'] = email.utils.localtime()
      msg.set_content(mail_body)

80 81 82
      mbox.add(msg)
      mbox.flush()

83 84 85
  finally:
    mbox.unlock()

86

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
def configure_cli_parser(parser):
  parser.add_argument(
    "-f", "--from",
    dest="sender", # Create attribute 'sender', since 'from' is a keyword
    type=str,
    help="E-mail sender name and address, as 'Name <user@server.tld>'",
    required=True)

  parser.add_argument(
    "-s", "--subject",
    type=str,
    help="E-mail subject",
    required=True)

  parser.add_argument(
    "-m", "--mbox-file",
    type=str,
    help="Target MBox file for e-mails to create. E.g. 'C:\\Program Files\\Thunderbird\\Data\\profile\\Mail\\Local Folders\\Unsent Messages'.",
    required=True)

  logutils.add_loglevel_argument(parser)


def main(
    cluster_files_dir=DEFAULT_CLUSTER_FILES_DIR,
    cluster_students_csv_file_glob=DEFAULT_CLUSTER_STUDENTS_CSV_FILE_GLOB,
    mail_body_file=DEFAULT_MAIL_BODY_FILE,
    students_receive_mails_as_header=DEFAULT_STUDENTS_RECEIVE_MAILS_AS_HEADER):

  parser = argparse.ArgumentParser()
  configure_cli_parser(parser)
  args = parser.parse_args()

  logutils.configure_level_and_format(args.log_level)


  sender_pattern = r"\s*(.+?)\s*<(.+?)@(.+?)>"
  sender_match = re.search(sender_pattern, args.sender)

  assert \
    sender_match and sender_match.lastindex == 3, \
    "Argument provided for --format does not match expected pattern"

  sender_address = Address(*sender_match.groups())

  create_emails(
    cluster_files_dir,
    cluster_students_csv_file_glob,
    mail_body_file,
    students_receive_mails_as_header,
    sender_address,
    args.subject,
    args.mbox_file)


142 143
if __name__ == "__main__":
  main()