In [19]:
from ahh import vis, ext, era, pre, sci
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import pandas as pd
import numpy as np
import datetime
import exifread
import os

In [20]:
EST_OFFSET = 1 # camera metadata still uses CST
GM1_OFFSET = -4 # the camera is 4 minutes faster
D7100_OFFSET = +4 # the camera is 4 minutes slower
START_DT_STR = '2017-07-08 08:00'
END_DT_STR = '2017-07-08 20:30'

FONT = ImageFont.truetype("data/GOTHIC.TTF", 100)
COLOR = (255, 255, 255, 0)
SHADOW_COLOR = (50, 50, 50, 0)

PLOT_TXT_FMT = 'Total Floors: {5:.0f}\nTotal Steps: {3:d}\nTotal Calories: {6:.0f}\nTotal Kilometers: {4:0.2f}\n\n' \
                   'Steps per Minute Avg: {0:.2f}\nBeats per Minute Avg: {1:.2f}\nKilometers per Hour Avg: {2:.2f}'
IMG_TXT_FMT = '{hour}:{dt.minute:02d} {ampm} EST {month} {dt.day}, {dt.year}\n' \
                  '{km:04.2f} km | {mi:04.2f} mi\n{steps:.0f} steps\n{bpm:.0f} bpm'

XLABEL = 'Time [EST]'
    
OVERVIEW_TITLE_FMT = 'Overview of July 8, 2017 from {hr1}:{min1} {ampm1} - {hr2}:{min2} {ampm2}'

In [44]:
def get_dt_from_img(fn):
    with open(fn, 'rb') as f:
        tags = exifread.process_file(f)
        time_mdata = tags['EXIF DateTimeOriginal']
        dt = (datetime.datetime.strptime(time_mdata.values, 
                                         '%Y:%m:%d %H:%M:%S')
              + datetime.timedelta(hours=EST_OFFSET))
        model = tags['Image Model']
        if 'GM1' in str(model):
            dt += datetime.timedelta(minutes=GM1_OFFSET)
        elif 'D7100' in str(model):
            dt += datetime.timedelta(minutes=D7100_OFFSET)
    return dt

def round_to_nearest_minute(dt):
    secs = dt.second
    if secs > 30:
        dt += datetime.timedelta(minutes=1)
    return dt.replace(second=0)

def plot_summary(df, base_dir, dts=None):
    if dts is not None:
        df = df.loc[df.index <= dts[-1] + datetime.timedelta(minutes=15)]
        mark = True
    elif dts is None:
        dts = pd.date_range(START_DT_STR, END_DT_STR, freq='1Min')
        mark = False

    steps_scale = [2 ** (n / 12.5) + 15 for n in df.steps]
    steps_scale = np.clip(np.array(steps_scale), 0, 2000)
    steps_avg = df.loc[df.steps > 0, 'steps'].mean()
    bpm_avg = df.loc[df.bpm > 0, 'bpm'].mean()
    dist_hourly_avg = df.resample('1H').sum()['distance'].mean()
    total_steps = df.steps.sum()
    total_calories = df.calories.sum()
    total_floors = df.floors.sum()
    total_beats = df.bpm.sum()
    total_distance = df.distance.sum()

    title_fmtd = OVERVIEW_TITLE_FMT.format(hr1=dts[0].strftime('%I'),
                                           min1=dts[0].strftime('%M'),
                                           ampm1=dts[0].strftime('%p'),
                                           hr2=dts[-1].strftime('%I'),
                                           min2=dts[-1].strftime('%M'),
                                           ampm2=dts[-1].strftime('%p')
                                          )

    ax = vis.plot_scatter(df.index, df['distance_cumsum'], c=df.bpm, s=steps_scale, figsize=(15, 7),
                          title=title_fmtd, label='Size Scaled to Steps per Minute', length_scale=False,
                          xlabel=XLABEL, ylabel='Total Distance [km]', cbar_label='Heart Rate [BPM]',
                          alpha=0.75, vmin=60, vmax=130, interval=10, pad=0.175, title_pad=1)
    
    plot_txt_fmtd = PLOT_TXT_FMT.format(steps_avg, bpm_avg, dist_hourly_avg,
                                        total_steps, total_distance,
                                        total_floors, total_calories)

    vis.set_axtext(ax, plot_txt_fmtd, loc='bottom right')
    base_dir = pre.mkdir(base_dir)
    
    if mark:
        for i, dt in enumerate(dts):
            vis.annotate_point(ax, dt,
                               df['distance_cumsum'][round_to_nearest_minute(dt)],
                               '<-- Pic {0}'.format(i + 1), size=12,
                               alpha=0.95, color=vis.COLORS['gray'], bbox=None)
    
    save = vis.savefig('{0}/overview.png'.format(base_dir), tight_layout=True)
    
def stamp_imgs(img_fp_list):
    dt_list = []
    for img_fp in img_fp_list:
        try:
            dt = get_dt_from_img(img_fp)
            dt_list.append(dt)
            dt_rounded = round_to_nearest_minute(dt)
            ds = df.loc[dt_rounded]
            mi = sci.convert(ds.distance_cumsum, km2mi=True)
            img_txt_fmtd = IMG_TXT_FMT.format(dt=dt_rounded,
                                              month=dt.strftime('%b'),
                                              hour=dt.strftime('%I'),
                                              ampm=dt.strftime('%p'),
                                              steps=ds.steps_cumsum,
                                              km=ds.distance_cumsum,
                                              mi=mi,
                                              bpm=ds.bpm
                                              )
            img = Image.open(img_fp)
            draw = ImageDraw.Draw(img)
            x, y, z = np.shape(img)
            draw.text((30, 20),
                      img_txt_fmtd,
                      SHADOW_COLOR,
                      font=FONT)
            draw.text((25, 15),
                      img_txt_fmtd,
                      COLOR,
                      font=FONT)
            img_stamped_fp = ext.append_to_fn(img_fp, '_stamped')
            img.save(img_stamped_fp)
            _ = os.system('mv {0} {1}'.format(img_fp, orig_dir))
        except:
            ext.report_err()
    return dt_list

def plot_steps_bpm(df, dts=None):
    if dts is not None:
        df = df.loc[df.index <= dts[-1]]
        mark = True
    elif dts is None:
        dts = pd.date_range(START_DT_STR, END_DT_STR, freq='1Min')
        mark = False

    title_fmtd = OVERVIEW_TITLE_FMT.format(hr1=dts[0].strftime('%I'),
                                           min1=dts[0].strftime('%M'),
                                           ampm1=dts[0].strftime('%p'),
                                           hr2=dts[-1].strftime('%I'),
                                           min2=dts[-1].strftime('%M'),
                                           ampm2=dts[-1].strftime('%p')
                                           )

    ax = vis.plot_line(df.index, df['bpm'], label='Beats per Minute', xlabel=XLABEL,
                       ylabel='Heart Rate / Pace', length_scale=False)
    ax = vis.plot_bar(df.index, df['steps'], alpha=0.5, bar_vals=False, label='Steps per Minute', 
                      ylim=(0, 175), xlim=(dts[0], dts[-1] + datetime.timedelta(minutes=10)),
                      color=vis.COLORS['blue'], width=0.0001, figsize='na', title=title_fmtd, length_scale=False)

    if mark:
        for i, dt in enumerate(dts):
            vis.annotate_point(ax, dt,
                               df['bpm'][round_to_nearest_minute(dt)],
                               '<-- Pic {0}'.format(i + 1), size=12,
                               alpha=0.95, color=vis.COLORS['gray'], bbox=None)
    
    save = vis.savefig('{0}/detailed.png'.format(base_dir), tight_layout=True)

In [45]:
df1 = pre.read_csv('data/activity_070817.csv', date='ACTIVITY DATE/TIME').resample('1Min').mean()
df2 = pre.read_csv('data/heart_070817.csv', date='HEART RATE DATE/TIME').resample('1Min').mean()
df = pre.merge(df1, df2).dropna(axis=0)
df.columns = 'calories, steps, distance, floors, bpm'.split(', ')
df = df.loc[df.index > START_DT_STR]
df['distance_cumsum'] = np.cumsum(df.distance)
df['steps_cumsum'] = np.cumsum(df.steps)

In [46]:
base_dir = 'aftermath'

plot_summary(df, base_dir)

In [47]:
base_dir = 'inopportune_start'
orig_dir = pre.mkdir(os.path.join(base_dir, 'original'))

img_fp_list = ext.glob('{0}/*.jpg'.format(base_dir))
dt_list = stamp_imgs(img_fp_list)
dts = pd.DatetimeIndex(dt_list)
plot_summary(df, base_dir, dts)
plot_steps_bpm(df, dts=dts)

In [ ]:


In [ ]: