In [1]:
from bokeh.io import output_notebook, show
output_notebook()


BokehJS successfully loaded.

In [2]:
# %load '../app/constants.py'
PALETTE_GREY_900 = '#212121'
PALETTE_GREY_700 = '#616161'
PALETTE_PINK_A400 = '#f50057'

COLOR_DARK_CONTRAST = 'white'

COLOR_PRIMARY = PALETTE_GREY_900
COLOR_PRIMARY_DARK = PALETTE_GREY_700
COLOR_ACCENT = PALETTE_PINK_A400

COLOR_PRIMARY_CONTRAST = COLOR_DARK_CONTRAST
COLOR_ACCENT_CONTRAST = COLOR_DARK_CONTRAST

In [3]:
# %load '../app/process_gtimelog.py'
import pandas as pd
import numpy as np


def get_raw_df():
    raw = pd.read_table('timelog.txt', quotechar=' ', sep=': ', names=['timestamp', 'activity'], engine='python',)

    # Set the column types

    raw.timestamp = pd.to_datetime(raw.timestamp)
    raw = raw.drop_duplicates()

    ### Build the times
    raw['end'] = raw.timestamp
    raw['start'] = raw['end'].shift(1)

    raw['start'] = np.where(
        raw['activity'] == 'start',  # If the activity is start
        raw['timestamp'],  # Set the start to timestamp
        raw['start'],  # Else leave it as start
    )

    raw['delta'] = raw.end - raw.start

    return raw


def keep_top_level_cats_only(gt_df):
    """
    Takes a gtimelog DataFrame, and removes the non-work categories, and
    boils the other categories down to their high-level category.
    """
    gt_df = gt_df[~gt_df.activity.str.contains(r'\*\*\*')]
    # Boil down the categories to the main work categories
    gt_df.activity = gt_df.activity.str.split(r' ').str[0]

    return gt_df

In [4]:
raw = get_raw_df()
raw.tail()


Out[4]:
timestamp activity end start delta
133 2015-07-06 11:14:00 oh the grind 2015-07-06 11:14:00 2015-07-06 11:00:00 00:14:00
134 2015-07-06 11:34:00 unicorns 2015-07-06 11:34:00 2015-07-06 11:14:00 00:20:00
135 2015-07-06 13:35:00 serious business 2015-07-06 13:35:00 2015-07-06 11:34:00 02:01:00
136 2015-07-06 13:53:00 unicorns 2015-07-06 13:53:00 2015-07-06 13:35:00 00:18:00
137 2015-07-06 20:20:00 serious business 2015-07-06 20:20:00 2015-07-06 13:53:00 06:27:00

In [5]:
import datetime

In [6]:
# today = datetime.date.today()
today = datetime.date(2015, 7, 6)
yesterday = datetime.date(2015, 7, 3)

In [7]:
just_today = raw[(raw.timestamp.dt.date == today) | (raw.timestamp.dt.date == yesterday)]
just_today = just_today[just_today.activity != 'start']
just_today['human'] = just_today.delta.dt.seconds / (60 * 60)
just_today.head()


Out[7]:
timestamp activity end start delta human
106 2015-07-03 00:54:00 serious business 2015-07-03 00:54:00 2015-07-03 00:00:00 00:54:00 0.900000
107 2015-07-03 02:54:00 ponies 2015-07-03 02:54:00 2015-07-03 00:54:00 02:00:00 2.000000
108 2015-07-03 03:05:00 ponies 2015-07-03 03:05:00 2015-07-03 02:54:00 00:11:00 0.183333
109 2015-07-03 04:17:00 ponies 2015-07-03 04:17:00 2015-07-03 03:05:00 01:12:00 1.200000
110 2015-07-03 04:27:00 ponies 2015-07-03 04:27:00 2015-07-03 04:17:00 00:10:00 0.166667

In [8]:
group_sum = just_today[['activity', 'timestamp', 'human']].groupby(['activity', just_today.timestamp.dt.date]).sum()
group_sum = group_sum.unstack('activity')
group_sum


Out[8]:
human
activity oh the grind ponies serious business unicorns
2015-07-03 0.283333 9.983333 1.766667 10.266667
2015-07-06 0.233333 NaN 8.466667 0.633333

In [9]:
print(group_sum.columns)
levels = group_sum.columns.levels
labels = group_sum.columns.labels
group_sum.columns = levels[1][labels[1]]
print(group_sum.columns)


MultiIndex(levels=[['human'], ['oh the grind', 'ponies', 'serious business', 'unicorns']],
           labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
           names=[None, 'activity'])
Index(['oh the grind', 'ponies', 'serious business', 'unicorns'], dtype='object', name='activity')

In [10]:
group_sum.sort_index(inplace=True)
group_sum.fillna(0, inplace=True)
group_sum


Out[10]:
activity oh the grind ponies serious business unicorns
2015-07-03 0.283333 9.983333 1.766667 10.266667
2015-07-06 0.233333 0.000000 8.466667 0.633333

In [11]:
group_sum.columns


Out[11]:
Index(['oh the grind', 'ponies', 'serious business', 'unicorns'], dtype='object', name='activity')

In [12]:
from bokeh.charts import Bar
from bokeh import palettes 
palette = getattr(palettes, 'Spectral%s' % len(group_sum.columns))

show(Bar(group_sum, palette=palette, legend=True))



In [13]:
bar = Bar(
    group_sum, tools='hover', legend=True,
    palette=palette, 
    width=600, height=300,
)
# Get chart items
from bokeh.models import Legend, LinearAxis, CategoricalAxis, GlyphRenderer, HoverTool
legend = bar.select({'type': Legend})
hover = bar.select({'type': HoverTool})
glyphs = bar.select({'type': GlyphRenderer})
xaxis = bar.select({'type': CategoricalAxis})
yaxis = bar.select({'type': LinearAxis})

In [14]:
# Format chart properties
bar.toolbar_location=None
bar.background_fill = COLOR_PRIMARY
bar.border_fill = COLOR_PRIMARY
bar.outline_line_color = None
bar.min_border_top = 0

In [15]:
legend.label_text_color = COLOR_PRIMARY_CONTRAST
legend.border_line_color = COLOR_PRIMARY_CONTRAST

hover.tooltips = [('hours', '$y')]
hover.point_policy = 'follow_mouse'

for g in glyphs:
    g.glyph.fill_alpha = 1
    g.glyph.line_color = None

In [16]:
from bokeh.models import DatetimeTickFormatter

# Set xaxis properties
xaxis.major_label_text_color = COLOR_PRIMARY_CONTRAST
xaxis.major_label_orientation = 0
xaxis.major_label_standoff = 15
xaxis.formatter = DatetimeTickFormatter(
    formats={
        'years': ["%a %d %b"],
    }
)                                               
xaxis.major_tick_out = None
xaxis.major_tick_in = None
xaxis.axis_line_color = None

In [17]:
# Set yaxis properties
yaxis.major_label_text_color = COLOR_PRIMARY_CONTRAST
yaxis.major_tick_out = None
yaxis.major_tick_in = None
yaxis.axis_line_color = None

In [18]:
show(bar)



In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]: