Skip to content

Commit

Permalink
update mailer to work with images
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoestner committed Aug 21, 2023
1 parent 96de464 commit 64a4482
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 80 deletions.
19 changes: 18 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
from dash.dash_table.Format import Format, Scheme

from folioflex.dashboard import dashboard_helper, layouts
from folioflex.dashboard.pages import (
Expand Down Expand Up @@ -839,7 +840,23 @@ def update_ManagerTable(manager_status, cq_pm):
if manager_status == "ready":
cq_pm = pd.read_json(cq_pm).reset_index()
cq_pm["lookback_date"] = pd.to_datetime(cq_pm["lookback_date"], unit="ms")
manager_table = layouts.manager_fmt, cq_pm.to_dict("records")
# formatting floats
# TODO: put this in dashboard_helper function
manager_table = [
{
"name": i,
"id": i,
**(
{
"type": "numeric",
"format": Format(precision=2, scheme=Scheme.fixed).group(True),
}
if cq_pm[i].dtype == "float64"
else {}
),
}
for i in cq_pm.columns
], cq_pm.to_dict("records")
else:
manager_table = (None, None)

Expand Down
67 changes: 0 additions & 67 deletions folioflex/dashboard/layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,73 +244,6 @@
),
]

manager_fmt = [
dict(id="index", name="index"),
dict(id="date", name="date"),
dict(id="lookback_date", name="lookback_date"),
dict(
id="cumulative_cost",
name="cumulative_cost",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(
id="market_value",
name="market_value",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(
id="return",
name="return",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(
id="dwrr_pct",
name="dwrr_pct",
type="numeric",
format=Format(precision=2, scheme=Scheme.percentage),
),
dict(
id="dwrr_ann_pct",
name="dwrr_ann_pct",
type="numeric",
format=Format(precision=2, scheme=Scheme.percentage),
),
dict(
id="realized",
name="realized",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(
id="unrealized",
name="unrealized",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(
id="cash",
name="cash",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(
id="equity",
name="equity",
type="numeric",
format=Format(precision=0, scheme=Scheme.fixed).group(True),
),
dict(id="benchmark", name="benchmark"),
dict(
id="benchmark_dwrr_pct",
name="benchmark_dwrr_pct",
type="numeric",
format=Format(precision=2, scheme=Scheme.percentage),
),
]

performance_fmt = [
dict(id="ticker", name="ticker"),
dict(id="date", name="date"),
Expand Down
2 changes: 2 additions & 0 deletions folioflex/portfolio/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,8 @@ def get_summary(self, date=None, lookbacks=None):
"""
if lookbacks is None:
lookbacks = [None]
elif isinstance(lookbacks, int):
lookbacks = [lookbacks]
portfolio_repr = ", ".join([portfolio.name for portfolio in self.portfolios])
logger.info(
f"Summarizing following portfolios: [{portfolio_repr}] with lookbacks {lookbacks}"
Expand Down
4 changes: 2 additions & 2 deletions folioflex/utils/cq.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def portfolio_query(config_file, broker="all", lookback=None):


@celery_app.task
def manager_query(config_file, lookback=None):
def manager_query(config_file, lookbacks=None):
"""Query for worker to generate manager.
Parameters
Expand All @@ -123,6 +123,6 @@ def manager_query(config_file, lookback=None):
config_path = config_helper.CONFIG_PATH / config_file

pm = portfolio.Manager(config_path=config_path)
cq_pm = pm.get_summary(lookback=lookback).to_json()
cq_pm = pm.get_summary(lookbacks=lookbacks).to_json()

return cq_pm
33 changes: 24 additions & 9 deletions folioflex/utils/mailer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
"""Emailer."""
"""
Email module.
This module contains functions to send emails as well as generate reports
to send in the emails.
"""

import base64
import datetime
import logging
import smtplib

from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

Expand All @@ -29,7 +35,7 @@
logger.addHandler(console_handler)


def send_email(message, subject, email_list):
def send_email(message, subject, email_list, image=None):
"""Send summary of portfolios to email.
Parameters
Expand All @@ -40,6 +46,8 @@ def send_email(message, subject, email_list):
Subject of email
email_list : list
Email addresses to send email to
image : bytes (optional) (default=None)
Image to attach to email
Returns
----------
Expand All @@ -62,6 +70,14 @@ def send_email(message, subject, email_list):
message = MIMEText(message, "html")
email.attach(message)

if image is not None:
email_image = MIMEImage(image)
email_image.add_header("Content-ID", "image1")
email_image.add_header(
"Content-Disposition", "attachment", filename="attach.png"
)
email.attach(email_image)

# Send the email
try:
with smtplib.SMTP(config_helper.SMTP_SERVER, config_helper.SMTP_PORT) as smtp:
Expand Down Expand Up @@ -115,20 +131,19 @@ def generate_report(
today = datetime.date.today()
subject = f"Summary as of {today}"
message = "Below is your financial summary.<br><br>"
image = None

if heatmap_dict is not None:
portfolio = heatmap_dict.get("portfolio", None)
lookback = heatmap_dict.get("lookback", None)

heatmap_summary = heatmap.get_heatmap(portfolio=portfolio, lookback=lookback)
# using plotly kaleido to convert to image into bytes then base64 encode as
# this is standard practice for emails.
heatmap_bytes = heatmap_summary.to_image(format="png")
heatmap_img = base64.b64encode(heatmap_bytes).decode("utf-8")
# using plotly kaleido to convert to image into bytes then attach it to the email.
image = heatmap_summary.to_image(format="png")

message += (
f"<p>Here is the heatmap as of {today}:</p>"
f"<img src='data:image/png;base64,{heatmap_img}'/>" + "<br>"
f"<img src='cid:image1' alt='heatmap'/>" + "<br>"
)

if manager_dict is not None:
Expand Down Expand Up @@ -169,4 +184,4 @@ def generate_report(
+ "<br>"
)

return send_email(message, subject=subject, email_list=email_list)
return send_email(message, subject=subject, email_list=email_list, image=image)
2 changes: 1 addition & 1 deletion folioflex/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = "0.4.1"
version = "0.1.0"

0 comments on commit 64a4482

Please sign in to comment.