mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-28 19:03:14 +01:00
287 lines
11 KiB
Python
287 lines
11 KiB
Python
"""UItility functions facilitate making unit testing easier and less brittle."""
|
|
|
|
|
|
from PIL import Image
|
|
import email
|
|
from email import encoders
|
|
from email.message import Message
|
|
from email.mime.base import MIMEBase
|
|
from email.mime.image import MIMEImage
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
import factory
|
|
import faker
|
|
from io import BytesIO
|
|
from numpy.random import randint
|
|
import random
|
|
import re
|
|
import string
|
|
import typing
|
|
from typing import Any, Optional, Tuple
|
|
import unicodedata
|
|
|
|
|
|
def strip_accents(text):
|
|
"""
|
|
Strip accents from input String. (only works on Pythin 3
|
|
|
|
:param text: The input string.
|
|
:type text: String.
|
|
|
|
:returns: The processed String.
|
|
:rtype: String.
|
|
"""
|
|
text = unicodedata.normalize('NFD', text)
|
|
text = text.encode('ascii', 'ignore')
|
|
text = text.decode("utf-8")
|
|
return str(text)
|
|
|
|
|
|
def text_to_id(text):
|
|
"""
|
|
Convert input text to id.
|
|
|
|
:param text: The input string.
|
|
:type text: String.
|
|
|
|
:returns: The processed String.
|
|
:rtype: String.
|
|
"""
|
|
text = strip_accents(text.lower())
|
|
text = re.sub('[ ]+', '_', text)
|
|
text = re.sub('[^0-9a-zA-Z_-]', '', text)
|
|
return text
|
|
|
|
|
|
def get_random_string(length: int=16) -> str:
|
|
return "".join(
|
|
[random.choice(string.ascii_letters + string.digits) for _ in range(length)]
|
|
)
|
|
|
|
|
|
def generate_random_image(image_format, array_dims):
|
|
"""
|
|
Creates an image from a random array.
|
|
|
|
:param image_format: An image format (PNG or JPEG).
|
|
:param array_dims: A tuple with array dimensions.
|
|
|
|
:returns: A byte string with encoded image
|
|
:rtype: bytes
|
|
"""
|
|
image_bytes = randint(low=0, high=255, size=array_dims, dtype='uint8')
|
|
io = BytesIO()
|
|
image_pil = Image.fromarray(image_bytes)
|
|
image_pil.save(io, image_format, subsampling=0, quality=100)
|
|
return io.getvalue()
|
|
|
|
|
|
def get_random_image(image_format: str="PNG", size: int=5):
|
|
"""
|
|
Returns a random image.
|
|
|
|
Args:
|
|
image_format: An image format (PNG or JPEG).
|
|
|
|
Returns:
|
|
A string with encoded image
|
|
"""
|
|
return generate_random_image(image_format, (size, size, 3))
|
|
|
|
|
|
def get_fake(provider: str, locale: str = "en_US", min_length: int = 5) -> Any:
|
|
"""
|
|
Generates a random string, float, integer etc based on provider
|
|
Provider can be "text', 'sentence', "word"
|
|
e.g. `get_fake('name')` ==> 'Buzz Aldrin'
|
|
"""
|
|
string = factory.Faker(provider).evaluate({}, None, {'locale': locale,})
|
|
while len(string) < min_length:
|
|
string += factory.Faker(provider).evaluate({}, None, {'locale': locale,})
|
|
return string
|
|
|
|
|
|
def get_fake_html(locale: str = "en_US", wrap_in_body_tag=True) -> Any:
|
|
"""
|
|
Generates a random string, float, integer etc based on provider
|
|
Provider can be "text', 'sentence',
|
|
e.g. `get_fake('name')` ==> 'Buzz Aldrin'
|
|
"""
|
|
html = factory.Faker("sentence").evaluate({}, None, {'locale': locale,})
|
|
for _ in range(0,4):
|
|
html += "<li>" + factory.Faker("sentence").evaluate({}, None, {'locale': locale,}) + "</li>"
|
|
for _ in range(0,4):
|
|
html += "<p>" + factory.Faker("text").evaluate({}, None, {'locale': locale,})
|
|
return f"<body>{html}</body>" if wrap_in_body_tag else html
|
|
|
|
def generate_email_address(
|
|
locale: str="en_US",
|
|
use_short_email: bool=False,
|
|
real_name_format: Optional[str]="{last_name}, {first_name}",
|
|
last_name_override: Optional[str]=None) -> Tuple[str, str, str, str]:
|
|
'''
|
|
Generate an RFC 2822 email address
|
|
|
|
:param locale: change this to generate locale specific names
|
|
:param use_short_email: defaults to false. If true then does not include real name in email address
|
|
:param real_name_format: pass a different format if different than "{last_name}, {first_name}"
|
|
:param last_name_override: override the fake name if you want some special characters in the last name
|
|
:returns <RFC2822 formatted email for header>, <short email address>, <first name>, <last_name
|
|
'''
|
|
fake = faker.Faker(locale=locale)
|
|
first_name = fake.first_name()
|
|
last_name = last_name_override or fake.last_name()
|
|
real_name = None if use_short_email else real_name_format.format(first_name=first_name, last_name=last_name)
|
|
# Add a random string to ensure we do not generate a real domain name
|
|
email_address = "{}.{}@{}".format(
|
|
first_name.replace(' ', '').encode("ascii", "ignore").lower().decode(),
|
|
last_name.replace(' ', '').encode("ascii", "ignore").lower().decode(),
|
|
get_random_string(5) + fake.domain_name()
|
|
)
|
|
# format email address for RFC 2822 and return
|
|
return email.utils.formataddr((real_name, email_address)), email_address, first_name, last_name
|
|
|
|
def generate_file_mime_part(locale: str="en_US",filename: str = None, content: str = None) -> Message:
|
|
"""
|
|
|
|
:param locale: change this to generate locale specific file name and attachment content
|
|
:param filename: pass a file name if you want to specify a specific name otherwise a random name will be generated
|
|
:param content: pass a string value if you want have specific content otherwise a random string will be generated
|
|
"""
|
|
part = MIMEBase('application', 'octet-stream')
|
|
part.set_payload(get_fake("text", locale=locale, min_length=1024) if content is None else content)
|
|
encoders.encode_base64(part)
|
|
if not filename:
|
|
filename = get_fake("word", locale=locale, min_length=8) + ".txt"
|
|
part.add_header('Content-Disposition', "attachment; filename=%s" % filename)
|
|
return part
|
|
|
|
def generate_executable_mime_part(locale: str="en_US",filename: str = None, content: str = None) -> Message:
|
|
"""
|
|
|
|
:param locale: change this to generate locale specific file name and attachment content
|
|
:param filename: pass a file name if you want to specify a specific name otherwise a random name will be generated
|
|
:param content: pass a string value if you want have specific content otherwise a random string will be generated
|
|
"""
|
|
part = MIMEBase('application', 'vnd.microsoft.portable-executable')
|
|
part.set_payload(get_fake("text", locale=locale, min_length=1024) if content is None else content)
|
|
encoders.encode_base64(part)
|
|
if not filename:
|
|
filename = get_fake("word", locale=locale, min_length=8) + ".exe"
|
|
part.add_header('Content-Disposition', "attachment; filename=%s" % filename)
|
|
return part
|
|
|
|
def generate_image_mime_part(locale: str="en_US",imagename: str = None, disposition_primary_type: str = "attachment") -> Message:
|
|
"""
|
|
|
|
:param locale: change this to generate locale specific file name and attachment content
|
|
:param filename: pass a file name if you want to specify a specific name otherwise a random name will be generated
|
|
"""
|
|
part = MIMEImage(generate_random_image(image_format="JPEG", array_dims=(200, 200)))
|
|
#part.set_payload(get_fake("text", locale=locale, min_length=1024))
|
|
encoders.encode_base64(part)
|
|
if not imagename:
|
|
imagename = get_fake("word", locale=locale, min_length=8) + ".jpg"
|
|
part.add_header('Content-Disposition', disposition_primary_type + "; filename= %s" % imagename)
|
|
return part
|
|
|
|
def generate_email_list(address_cnt: int = 3,
|
|
locale: str="en_US",
|
|
use_short_email: bool=False
|
|
) -> str:
|
|
"""
|
|
Generates a list of email addresses formatted for email headers on a Mime part
|
|
|
|
:param address_cnt: the number of email addresses to string together
|
|
:param locale: change this to generate locale specific "real names" and subject
|
|
:param use_short_email: produces a email address without "real name" if True
|
|
|
|
"""
|
|
email_address_list = [generate_email_address(locale, use_short_email=use_short_email)[0] for _ in range(0, address_cnt)]
|
|
return ",".join(email_address_list)
|
|
|
|
def add_simple_email_headers(message: Message, locale: str="en_US",
|
|
use_short_email: bool=False
|
|
) -> typing.Tuple[typing.Tuple[str, str], typing.Tuple[str, str]]:
|
|
"""
|
|
Adds the key email headers to a Mime part
|
|
|
|
:param message: the Mime part to add headers to
|
|
:param locale: change this to generate locale specific "real names" and subject
|
|
:param use_short_email: produces a "To" or "From" that is only the email address if True
|
|
|
|
"""
|
|
to_meta = generate_email_address(locale, use_short_email=use_short_email)
|
|
from_meta = generate_email_address(locale, use_short_email=use_short_email)
|
|
|
|
message['Subject'] = get_fake("sentence", locale=locale)
|
|
message['From'] = from_meta[0]
|
|
message['To'] = to_meta[0]
|
|
return from_meta, to_meta
|
|
|
|
def generate_mime_part(locale: str="en_US",
|
|
part_type: str="plain",
|
|
) -> typing.Optional[Message]:
|
|
"""
|
|
Generates amime part of the sepecified type
|
|
|
|
:param locale: change this to generate locale specific strings
|
|
:param text_type: options are plain, html, image (attachment), file (attachment)
|
|
"""
|
|
if "plain" == part_type:
|
|
body = get_fake("text", locale=locale, min_length=1024)
|
|
msg = MIMEText(body, part_type)
|
|
elif "html" == part_type:
|
|
body = get_fake_html(locale=locale, wrap_in_body_tag=True)
|
|
msg = MIMEText(body, part_type)
|
|
elif "file" == part_type:
|
|
msg = generate_file_mime_part(locale=locale)
|
|
elif "executable" == part_type:
|
|
msg = generate_executable_mime_part(locale=locale)
|
|
elif "image" == part_type:
|
|
msg = generate_image_mime_part(locale=locale)
|
|
else:
|
|
raise Exception("Mime part not implemented: " + part_type)
|
|
return msg
|
|
|
|
def generate_multipart_email(locale: str="en_US",
|
|
type_list: typing.List[str]=["plain", "html", "image"],
|
|
sub_type: str = None,
|
|
use_short_email: bool=False
|
|
) -> typing.Tuple[Message, typing.Tuple[str, str], typing.Tuple[str, str]]:
|
|
"""
|
|
Generates an email including headers with the defined multiparts
|
|
|
|
:param locale:
|
|
:param type_list: options are plain, html, image (attachment), file (attachment), and executable (.exe attachment)
|
|
:param sub_type: multipart sub type that defaults to "mixed" if not specified
|
|
:param use_short_email: produces a "To" or "From" that is only the email address if True
|
|
"""
|
|
msg = MIMEMultipart(sub_type) if sub_type else MIMEMultipart()
|
|
for part_type in type_list:
|
|
msg.attach(generate_mime_part(locale=locale, part_type=part_type))
|
|
from_meta, to_meta = add_simple_email_headers(msg, locale=locale, use_short_email=use_short_email)
|
|
return msg, from_meta, to_meta
|
|
|
|
def generate_text_email(locale: str="en_US",
|
|
use_short_email: bool=False
|
|
) -> typing.Tuple[Message, typing.Tuple[str, str], typing.Tuple[str, str]]:
|
|
"""
|
|
Generates an email including headers
|
|
"""
|
|
body = get_fake("text", locale=locale, min_length=1024)
|
|
msg = MIMEText(body)
|
|
from_meta, to_meta = add_simple_email_headers(msg, locale=locale, use_short_email=use_short_email)
|
|
return msg, from_meta, to_meta
|
|
|
|
def generate_html_email(locale: str="en_US",
|
|
use_short_email: bool=False
|
|
) -> typing.Tuple[Message, typing.Tuple[str, str], typing.Tuple[str, str]]:
|
|
"""
|
|
Generates an email including headers
|
|
"""
|
|
body = get_fake_html(locale=locale)
|
|
msg = MIMEText(body)
|
|
from_meta, to_meta = add_simple_email_headers(msg, locale=locale, use_short_email=use_short_email)
|
|
return msg, from_meta, to_meta
|