Now with the an email send endpoint!

This commit is contained in:
Scott Idem
2023-06-27 16:03:08 -04:00
parent e91e639112
commit 2bbc219df3
4 changed files with 243 additions and 11 deletions

View File

@@ -412,6 +412,7 @@ def send_email(
to_email: str, to_email: str,
subject: str, subject: str,
body_html: str, body_html: str,
from_name: str = '', from_name: str = '',
reply_to_email: str = '', reply_to_email: str = '',
reply_to_name: str = '', reply_to_name: str = '',
@@ -421,6 +422,8 @@ def send_email(
bcc_email: str = '', bcc_email: str = '',
bcc_name: str = '', bcc_name: str = '',
body_text: str = '', body_text: str = '',
test: bool = False
): ):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals()) log.debug(locals())
@@ -430,21 +433,76 @@ def send_email(
message['Subject'] = subject message['Subject'] = subject
else: else:
return False return False
if from_email:
if from_email and from_name:
#message['From'] = Address(display_name=from_name, username=from_email.split('@')[0], domain=from_email.split('@')[1]) #message['From'] = Address(display_name=from_name, username=from_email.split('@')[0], domain=from_email.split('@')[1])
message['From'] = Address(display_name=from_name, addr_spec=from_email) try:
message['From'] = Address(display_name=from_name, addr_spec=from_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
elif from_email:
try:
message['From'] = Address(display_name=from_email, addr_spec=from_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
else: else:
return False return False
if reply_to_email:
message['Reply-To'] = Address(display_name=reply_to_name, addr_spec=reply_to_email) if reply_to_email and reply_to_name:
if to_email: try:
message['To'] = Address(display_name=to_name, addr_spec=to_email) message['Reply-To'] = Address(display_name=reply_to_name, addr_spec=reply_to_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
elif reply_to_email:
try:
message['Reply-To'] = Address(display_name=reply_to_email, addr_spec=reply_to_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
if to_email and to_name:
try:
message['To'] = Address(display_name=to_name, addr_spec=to_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
elif to_email:
try:
message['To'] = Address(display_name=to_email, addr_spec=to_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
else: else:
return False return False
if cc_email:
message['Cc'] = Address(display_name=cc_name, addr_spec=cc_email) if cc_email and cc_name:
if bcc_email: try:
message['Bcc'] = Address(display_name=bcc_name, addr_spec=bcc_email) message['Cc'] = Address(display_name=cc_name, addr_spec=cc_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
elif cc_email:
try:
message['Cc'] = Address(display_name=cc_email, addr_spec=cc_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
if bcc_email and bcc_name:
try:
message['Bcc'] = Address(display_name=bcc_name, addr_spec=bcc_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
elif bcc_email:
try:
message['Bcc'] = Address(display_name=bcc_email, addr_spec=bcc_email)
except Exception as e:
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
return False
html_version = """\ html_version = """\
<html> <html>
@@ -479,6 +537,21 @@ def send_email(
# log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL # log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.info('SMTP configuration, connect, and send') log.info('SMTP configuration, connect, and send')
if test:
try:
with smtplib.SMTP_SSL(settings.SMTP['server'], settings.SMTP['port'], context=context) as server:
log.debug('[TEST] SMTP log in...')
server.login(settings.SMTP['username'], settings.SMTP['password'])
log.debug('[TEST] SMTP send message... [WILL NOT SEND IN TEST MODE]')
# server.send_message(message)
log.info('[TEST] Email sent!')
return True
except:
#except SMTPException:
log.error('[TEST] Error: unable to send email')
return False
try: try:
with smtplib.SMTP_SSL(settings.SMTP['server'], settings.SMTP['port'], context=context) as server: with smtplib.SMTP_SSL(settings.SMTP['server'], settings.SMTP['port'], context=context) as server:
log.debug('SMTP log in...') log.debug('SMTP log in...')

View File

@@ -18,7 +18,7 @@ from . import config
from app.log import log, logging from app.log import log, logging
# Import the routers here first: # Import the routers here first:
from app.routers import aether_cfg, api_crud, api, importing, sql, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, data_store, event, event_abstract, event_badge, event_badge_importing, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, fundraising, grant, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, qr, site, site_domain, user, websockets_redis, e_confex, e_cvent, c_idaa, e_impexium, e_stripe from app.routers import aether_cfg, api_crud, api, importing, sql, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, data_store, event, event_abstract, event_badge, event_badge_importing, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, fundraising, grant, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, qr, site, site_domain, user, util_email, websockets_redis, e_confex, e_cvent, c_idaa, e_impexium, e_stripe
# from app.routers import aether_cfg, sql # from app.routers import aether_cfg, sql
@@ -391,6 +391,10 @@ app.include_router(
user.router, user.router,
tags=['User'], tags=['User'],
) )
app.include_router(
util_email.router,
tags=['Utility: Email'],
)
# app.include_router( # app.include_router(
# websockets.router, # websockets.router,
# # prefix='/websocket', # # prefix='/websocket',

View File

@@ -0,0 +1,78 @@
import datetime, pytz
from typing import Dict, List, Optional, Set, Union
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator
from app.db_sql import redis_lookup_id_random
from app.lib_general import log, logging
from app.models.common_field_schema import base_fields, default_num_bytes
from app.models.core_object_models import Core_Std_Obj_Base
# ### BEGIN ### API Utility: Email Models ### Email_Send_Base() ###
# Update 2023-06-27
class Email_Send_Base(BaseModel):
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
id_random: Optional[str] = Field(
# **base_fields['email_id_random'],
alias = 'email_id_random',
)
id: Optional[int] = Field(
alias = 'email_id'
)
account_id_random: Optional[str]
account_id: Optional[int]
testing: Optional[bool] = False
from_email: str # = 'from_test@example.com'
from_name: str = None
to_email: str # = 'to_test@example.com'
to_name: str = None
cc_email: str = None
cc_name: str = None
bcc_email: str = None
bcc_name: str = None
subject: str
body_html: str
body_text: str = None
# json: Optional[str] # "json" is reserved; need to change field name? json_str?
# json_str: Optional[Union[Json, None]] = Field(
# alias = 'json',
# )
# text: Optional[str]
# notes: Optional[str]
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
@validator('id', always=True)
def email_id_lookup(cls, v, values, **kwargs):
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='email')
return None
@validator('account_id', always=True)
def account_id_lookup(cls, v, values, **kwargs):
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('account_id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='account')
return None
class Config:
underscore_attrs_are_private = True
allow_population_by_field_name = True
fields = base_fields
# ### END ### API Grant Models ### Grant_Base() ###

77
app/routers/util_email.py Normal file
View File

@@ -0,0 +1,77 @@
import datetime, pytz, time
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response, status
from pydantic import BaseModel, EmailStr, Field
from typing import Dict, List, Optional, Set, Union
from app.lib_general import log, logging, common_route_params, Common_Route_Params, send_email
from app.config import settings
from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, get_id_random, redis_lookup_id_random
from app.routers.api_crud import delete_obj_template, get_obj_template, get_obj_li_template, patch_obj_template, post_obj_template
from app.methods.data_store_methods import create_update_data_store_obj, get_data_store_rec_list, load_data_store_obj, load_data_store_obj_w_code
# from app.models.common_field_schema import default_num_bytes
from app.models.util_email_models import Email_Send_Base
from app.models.response_models import Resp_Body_Base, mk_resp
router = APIRouter()
# ### BEGIN ### API Utility: Email ### util_email_send_obj() ###
# Updated 2023-06-27
@router.post('/util/email/send', response_model=Resp_Body_Base)
async def util_email_send_obj(
email_send_obj: Email_Send_Base,
test: bool = False,
return_obj: bool = True,
commons: Common_Route_Params = Depends(common_route_params),
):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
resp_data = {}
from_email = email_send_obj.from_email
resp_data['from_email'] = from_email
from_name = email_send_obj.from_name
to_email = email_send_obj.to_email
resp_data['to_email'] = to_email
to_name = email_send_obj.to_name
bcc_email = email_send_obj.bcc_email
bcc_name = email_send_obj.bcc_name
subject = email_send_obj.subject
resp_data['subject'] = subject[:20]
body_html = email_send_obj.body_html
body_text = email_send_obj.body_text
log.info('Trying send_email()...')
# NOTE: This all works, but I think it could be done better???
if send_email(from_email=from_email, from_name=from_name, to_email=to_email, to_name=to_name, bcc_email=bcc_email, bcc_name=bcc_name, subject=subject, body_text=body_text, body_html=body_html, test=test):
status_code = 200
status_message = f'Email was sent to <{to_email}>.'
else:
status_code = 400
status_message = f'Email failed to send to <{to_email}>.'
log.info(status_message)
if return_obj:
return mk_resp(data=resp_data, status_code=status_code, response=commons.response, status_message=status_message)
else:
if status_code == 200:
resp_data = True
else:
resp_data = False
return mk_resp(data=resp_data, status_code=status_code, response=commons.response, status_message=status_message)
# ### END ### API Utility: Email ### util_email_send_obj() ###