Coverage for portality/app_email.py: 33%

69 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-04 15:38 +0100

1# ~~Email:Library~~ 

2from flask import render_template, flash 

3from flask_mail import Mail, Message, Attachment 

4from portality.core import app 

5 

6import uuid 

7import markdown 

8 

9 

10class EmailException(Exception): 

11 pass 

12 

13 

14def send_markdown_mail(to, fro, subject, template_name=None, bcc=None, files=None, msg_body=None, markdown_template_name=None, **template_params): 

15 html_body = None 

16 if markdown_template_name: 

17 try: 

18 markdown_body = render_template(markdown_template_name, **template_params) 

19 except: 

20 with app.test_request_context(): 

21 markdown_body = render_template(markdown_template_name, **template_params) 

22 

23 md = markdown.Markdown() 

24 html_body = md.convert(markdown_body) 

25 

26 send_mail(to, fro, subject, template_name=template_name, bcc=bcc, files=files, msg_body=msg_body, html_body=html_body, **template_params) 

27 

28# Flask-Mail version of email service from util.py 

29def send_mail(to, fro, subject, template_name=None, bcc=None, files=None, msg_body=None, html_body=None, **template_params): 

30 """ 

31 ~~-> Email:ExternalService~~ 

32 ~~-> FlaskMail:Library~~ 

33 :param to: 

34 :param fro: 

35 :param subject: 

36 :param template_name: 

37 :param bcc: 

38 :param files: 

39 :param msg_body: 

40 :param template_params: 

41 :return: 

42 """ 

43 bcc = [] if bcc is None else bcc 

44 files = [] if files is None else files 

45 

46 # ensure that email isn't sent if it is disabled 

47 if not app.config.get("ENABLE_EMAIL", False): 

48 app.logger.info("Email template {0} called to send, but email has been disabled.\nto:{1}\tsubject:{2}".format(template_name, to, subject)) 

49 return 

50 

51 assert type(to) == list 

52 assert type(files) == list 

53 if bcc and not isinstance(bcc, list): 

54 bcc = [bcc] 

55 

56 to_is_invalid = True 

57 for t in to: 

58 if t: 

59 to_is_invalid = False 

60 # a list of None, None, None or even just a [None] is no good! 

61 

62 if to_is_invalid: 

63 magic = str(uuid.uuid1()) 

64 app.logger.error('Bad To list while trying to send email with subject \"{0}\". Magic num for log grep {1}'.format(subject, magic)) 

65 flash("Invalid email address - no email specified at all. Trying to send email with subject \"{0}\". Magic number to help identify error: {1}".format(subject, magic), 'error') 

66 return 

67 

68 if app.config.get('CC_ALL_EMAILS_TO', None) is not None: 

69 bcc.append(app.config.get('CC_ALL_EMAILS_TO')) 

70 

71 # Get the body text from the msg_body parameter (for a contact form), 

72 # or render from a template. 

73 # TODO: This could also find and render an HTML template if present 

74 if msg_body: 

75 plaintext_body = msg_body 

76 else: 

77 try: 

78 plaintext_body = render_template(template_name, **template_params) 

79 except: 

80 with app.test_request_context(): 

81 plaintext_body = render_template(template_name, **template_params) 

82 

83 # strip all the leading and trailing whitespace from the body, which the templates 

84 # leave lying around 

85 plaintext_body = plaintext_body.strip() 

86 

87 # create a message 

88 msg = Message(subject=subject, 

89 recipients=to, 

90 body=plaintext_body, 

91 html=html_body, 

92 sender=fro, 

93 cc=None, 

94 bcc=bcc, 

95 attachments=files, 

96 reply_to=None, 

97 date=None, 

98 charset=None, 

99 extra_headers=None 

100 ) 

101 

102 try: 

103 mail = Mail(app) 

104 with app.app_context(): 

105 mail.send(msg) 

106 app.logger.info("Email template {0} sent.\nto:{1}\tsubject:{2}".format(template_name, to, subject)) 

107 except Exception as e: 

108 raise EmailException(e) 

109 

110 

111def make_attachment(filename, content_type, data, disposition=None, headers=None): 

112 """ 

113 Provide a function which can make attachments, insulating the caller from the flask-mail 

114 underlying implementation. 

115 

116 :param filename: 

117 :param content_type: 

118 :param data: 

119 :param disposition: 

120 :param headers: 

121 :return: 

122 """ 

123 return Attachment(filename, content_type, data, disposition, headers) 

124 

125 

126def email_archive(data_dir, archv_name): 

127 """ 

128 Compress and email the reports to the specified email address. 

129 :param data_dir: Directory containing the reports 

130 :param archv_name: Filename for the archive and resulting email attachment 

131 """ 

132 import shutil, os 

133 

134 email_to = app.config.get('REPORTS_EMAIL_TO', ['helpdesk@doaj.org']) 

135 email_from = app.config.get('SYSTEM_EMAIL_FROM', 'helpdesk@doaj.org') 

136 email_sub = app.config.get('SERVICE_NAME', '') + ' - generated {0}'.format(archv_name) 

137 msg = "Attached: {0}.zip\n".format(archv_name) 

138 

139 # Create an archive of the reports 

140 archv = shutil.make_archive(archv_name, "zip", root_dir=data_dir) 

141 

142 # Read the archive to create an attachment, send it with the app email 

143 with open(archv, 'rb') as f: 

144 dat = f.read() 

145 attach = [make_attachment(filename=archv_name, content_type='application/zip', data=dat)] 

146 send_mail(to=email_to, fro=email_from, subject=email_sub, msg_body=msg, files=attach) 

147 

148 # Clean up the archive 

149 os.remove(archv)