Morning Report

Home Projects Research About

My morning report is a text file printed every morning on my dot-matrix printer. It contains:

This is done by 3 supporting Python scripts, a shell script to put them all together, and a cron job to automate it.

cron job

The cron job merely runs the shell script daily at 07:45 and redirects the output to the printer:

45 7 * * * ~/projects/morning-report/morning-report.sh > /dev/usb/lp0 2> ~/projects/log/error.txt

Note that stderr is redirected to an error logging file, for debugging purposes.

morning-report.sh

This script puts the results of all the other scripts together in a format that is nice for my printer to print, outputting it to stdout.

#!/bin/bash

cd 
~/projects/morning-report
. bin/activate
echo 
-ne "\x1B\x67" #switch to 15cpi
python3 log
-report.py > log-report
python3 weather
-report.py > weather-report
paste log
-report weather-report | column -s $'\t' -tn
python3 news
-report.py > news-report
cat news
-report
It first sources the Python3 virtual environment for this project on my raspberry pi, then it outputs a command that makes the printer switch the font size, then it runs log-report.py and weather-report.py, column-stacks and outputs them, and finally runs news-report.py and outputs it.

Source: morning-report.sh

log-report.py

log-report.py is a Python script which downloads my productivity data, makes some calculations and outputs it, and also downloads my calendar, interprets it, and outputs some useful information.

from pyexcel_ods import get_data
import numpy as np
import pandas as pd
from datetime import datetime, date
from pytz import timezone
from icalendar import Calendar, Event
import requests
import recurring_ical_events
import calendar

current
=datetime.now(timezone('UTC'))
print(str(current.date())+", "+calendar.day_name[current.weekday()]+"\n")
2021-03-08, Monday

r=requests.get("[log url]")
with open("logv2.ods","wb") as f:
    f
.write(r.content)

spread
=get_data("logv2.ods")

data
=spread['Sheet1']

columns
=["D","WD","startutc","startlocal","activity","detail","people","super","spent","","subplace","place","city","country","spentint"]

df
=pd.DataFrame(data,columns=columns)
df
.drop([0],inplace=True)
df
.drop([""],axis=1,inplace=True)
df
.reset_index(inplace=True,drop=True)
df
.drop(df.tail(2).index, inplace = True) #drop last 2 rows

df
['activity'].replace('', np.nan, inplace=True)
df
.dropna(subset=['activity'], inplace=True)

def get_n(threshold):
    length
=len(df["startutc"])
    n
=min(threshold*120,length-2)
    delta
=df["startutc"][length-1]-df["startutc"][length-n]
    days
=delta.total_seconds()/3600/24
    
while(days>=threshold):
        n
-=1
        delta
=df["startutc"][length-1]-df["startutc"][length-n]
        days
=delta.total_seconds()/3600/24
    
return n

labels
=["productive","fun","routine","waste"]
filters
=[{LIST OF PRODUCTIVE STRINGS},
         
{LIST OF FUN STRINGS},
         
{LIST OF ROUTINE STRINGS},
         
{LIST OF WASTE STRINGS}]

def get_times(n):
    i
=n
    total
=0
    times
=np.zeros(4)
    total
=0
    length
=len(df["startutc"])
    
for i in range(2,n+1):
        activity
=df["activity"][length-i]
        time
=df["spentint"][length-i]
        
if activity != "sleep":
            total
+=time
        
for j in range (0, len(labels)):
            
if activity in filters[j]:
                times
[j]+=time

    times
=times/total
    times
=[round(t,3) for t in times]
#     for i in range(0,len(times)):
#         times[i]=round(times[i],3)
    
return times

def output_times(days, timess):
    df
=pd.DataFrame(timess.T,columns=days,index=labels)
    df
.columns.name='past days'
    
print(df)

def print_times(dayss):
    timess
=[]
    
for days in dayss:
        timess
.append(get_times(get_n(days)))
    output_times
(dayss,np.array(timess))

print_times
([1,7,30,365,df['D'].iloc[-1]])
past days    1      7      30     365    1027
productive  
0.163  0.044  0.181  0.229  0.206
fun         
0.552  0.596  0.396  0.298  0.180
routine     
0.072  0.082  0.072  0.068  0.069
waste       
0.058  0.101  0.080  0.111  0.089
r=requests.get("[webDAV calendar URL]")
with open("cal.ics","wb") as f:
    f
.write(r.content)


cal
=Calendar.from_ical(open("cal.ics","rb").read())

recurring 
= recurring_ical_events.of(cal).at(date.today())
def eventcompare(e):
    
return e["DTSTART"].dt
recurring
.sort(key=eventcompare)

print("\nCALENDAR")
for component in recurring:
    start
=component["DTSTART"].dt.time()
    
end=component["DTEND"].dt.time()
    
print(str(start)+"-"+str(end)+" "+component["SUMMARY"])

CALENDAR
08:10:00-10:00:00 cc
19:10:00-20:25:00 linreg
print("\nTODO")
def todocompare(e):
    
return e["DUE"].dt
eventsort
=[for e in cal.walk() if e.name=="VTODO" and e["STATUS"]!="COMPLETED"]
eventsort
.sort(key=todocompare)
for component in eventsort:
    
if component.name=="VTODO" and component["STATUS"]!="COMPLETED":
        
#print(component)
        dt
=component["DUE"].dt
        
print(str(dt.date())+" "+str(dt.time())+" "+component["SUMMARY"])

TODO
2021-03-08 14:00:00 groceries
2021-03-10 08:00:00 fundies-hw5
2021-03-13 23:59:00 bayesian-hw4

Source: log-report.py

weather-report.py

weather-report.py is a Python script which makes a request to the OpenWeatherMap API and outputs some useful weather information.

#!/usr/bin/env python

import requests
import numpy as np
import pandas as pd
from datetime import datetime, date

r
=requests.get('https://api.openweathermap.org/data/2.5/onecall?lat=LATITUDE&lon=LONGITUDE&exclude=minutely,daily&appid=APIKEY')
data
=r.json()

sunrise
=str(datetime.fromtimestamp(data["current"]["sunrise"]).time())
sunset
=str(datetime.fromtimestamp(data["current"]["sunset"]).time())
print("sunrise:"+sunrise+"  sunset:"+sunset)

sunrise
:06:18:30  sunset:17:54:39
hourly=np.array(data['hourly'])[:24]

times
=[datetime.fromtimestamp(hour["dt"]).time() for hour in hourly]
temps
=[round(hour["temp"]-273.15) for hour in hourly]
feels_likes
=[round(hour["feels_like"]-273.15) for hour in hourly]
descs
=[hour["weather"][0]['description'] for hour in hourly]
weather
=pd.DataFrame(list(zip(times,temps,feels_likes,descs)),columns=["Time","Temp","Feels","Description"])

print(weather.to_string(index=False)+"\n")
    Time  Temp  Feels      Description
13:00:00     3     -2        clear sky
14:00:00     3     -2        clear sky
15:00:00     4     -1        clear sky
16:00:00     4      0        clear sky
17:00:00     5      1        clear sky
18:00:00     4      1        clear sky
19:00:00     4      0        clear sky
20:00:00     3     -1        clear sky
21:00:00     3     -1 scattered clouds
22:00:00     3     -1 scattered clouds
23:00:00     3     -2       few clouds
00:00:00     3     -2 scattered clouds
01:00:00     3     -2 scattered clouds
02:00:00     3     -2 scattered clouds
03:00:00     3     -3    broken clouds
04:00:00     3     -2    broken clouds
05:00:00     4     -2    broken clouds
06:00:00     4     -2  overcast clouds
07:00:00     4     -1    broken clouds
08:00:00     5      0        clear sky
09:00:00     7      2        clear sky
10:00:00     9      4        clear sky
11:00:00    10      4        clear sky
12:00:00    10      5        clear sky

Source: weather-report.py

news-report.py

news-report.py is a Python script which uses RSS to grab some news headlines and upcoming rocket launches.

#!/usr/bin/env python
# coding: utf-8

import feedparser
from datetime import datetime, date
from time import mktime
import requests
import numpy as np

urls
=["rss feed url 1","rss feed url 2"…]

feeds
=[]
for url in urls:
    feeds
.append(feedparser.parse(url))

now
=datetime.now()
for d in feeds:
    entries
=[]
    
for entry in d['entries']:
        ts
=entry['published_parsed']
        
#print(ts)
        delta
=now-datetime.fromtimestamp(mktime(ts))
        days
=delta.total_seconds()/60/60/24
        
if(days<1):
            entries
.append(entry['title'])
    
if entries:
        
print(d['feed']['title']+"---------------------------")
        
for entry in entries:
            
print(entry)

print("rockets:-----------------")
r
=requests.get('https://fdo.rocketlaunch.live/json/launches/next/5')
data
=np.array(r.json()['result'])[:2]
for launch in data:
    date
=str(datetime.fromtimestamp(int(launch['sort_date'])))
    provider
=launch['provider']['name']
    vehicle
=launch['vehicle']['name']
    mission
=launch['missions'][0]['name']
    
print(date+": "+provider+" "+vehicle+". "+launch['missions'][0]['name'])

print("\x1BJ\xD8\x1BJ\xD8\x1BJ\x6C")

Note that, at the very end, it also outputs some bytes which tell the printer to feed the paper out a few centimeters.

Source: news-report.py