Commit 3f04ec12 authored by Saswat's avatar Saswat

Initial commit

parents
{
"INTERVAL": 60,
"DATABASE_FILE": "data/activity_data.db",
"LOG_FILE": "log/deamon.log"
}
\ No newline at end of file
import sqlite3
from utils import load_config
from datetime import datetime, timedelta
#signal.signal(signal.SIGINT, handler)
class DBHandler:
def __init__(self, db_path) -> None:
self.conn = sqlite3.connect(db_path)
self.check_activity_table()
return
def check_activity_table(self):
listOfTables = self.conn.execute(
'''SELECT name FROM sqlite_master WHERE type='table'
AND name='ACTIVITY';''').fetchall()
if listOfTables == []:
print('Creating new table')
self.conn.execute(''' CREATE TABLE ACTIVITY
(DATETIME INT NOT NULL,
APPID TEXT NOT NULL,
EXECTIME INT NOT NULL);
''')
return
def close_connection(self):
self.conn.close()
return
def print_db(self):
print("All data in activity table")
cursor = self.conn.execute("SELECT * from ACTIVITY ")
for row in cursor:
print(row)
return
def write_interval(self, interval_start, app_name, duration):
query = f"INSERT INTO activity VALUES ({interval_start}, '{app_name}', {duration})"
print(query)
self.conn.execute(query)
self.conn.commit()
def read_week_data(self, date:datetime, app_name=""):
start = date - timedelta(days=date.weekday())
end = start + timedelta(days=6)
print(start)
print(end)
start = start.timestamp()
end = end.timestamp()
if app_name:
query = f'''SELECT * from ACTIVITY where DATETIME >= {start} and DATETIME <= {end} and APPID == "{app_name}"'''
else:
query = f"SELECT * from ACTIVITY where DATETIME >= {start} and DATETIME <= {end}"
cursor = self.conn.cursor()
data = cursor.execute(query).fetchall()
return data
\ No newline at end of file
import time
from dbhandler import DBHandler
from utils import extract_app_name
class Interval:
def __init__(self, interval, db:DBHandler) -> None:
self.__proc_duration = dict() # dict that stores processes duration in an interval
self.duration = int(interval) # Interval duration
self.db = db
self.start = int(self.__get_current_time() / self.duration) * self.duration # start of the interval
self.remaining_time = self.duration
return
def get_remaining_time(self):
return self.duration - self.exec
def __get_current_time(self):
return int(time.time())
def add_process(self, title:str, exec_time:int):
self.remaining_time -= exec_time
if title in self.__proc_duration:
self.__proc_duration[title] += exec_time
else:
self.__proc_duration[title] = exec_time
def dump_interval(self, incr_interval=False):
for title in self.__proc_duration:
self.db.write_interval(self.start, title, self.__proc_duration[title])
self.reset(incr_interval)
return
def reset(self, incr_interval=False):
self.__proc_duration.clear()
if incr_interval:
self.start += self.duration
else:
self.start = int(self.__get_current_time() / self.duration) * self.duration
self.remaining_time = self.duration
class EventHandler:
def __init__(self, interval, db_file) -> None:
self.__prev_event_time = self.__get_current_time() # store current time as prev time to use later
self.__interval = Interval(interval, DBHandler(db_file))
self.__prev_proc = ""
return
def __get_current_time(self):
return int(time.time())
def handle_change(self, title:str) -> None:
curr_event_time = self.__get_current_time()
last_proc_exec_time = curr_event_time - self.__prev_event_time
while(last_proc_exec_time > self.__interval.remaining_time):
prev_int_rem_time = last_proc_exec_time - self.__interval.remaining_time
self.__interval.add_process(self.__prev_proc, self.__interval.remaining_time)
self.__interval.dump_interval(incr_interval=True)
last_proc_exec_time = prev_int_rem_time
self.__interval.add_process(self.__prev_proc, last_proc_exec_time)
self.__prev_event_time = curr_event_time
self.__prev_proc = extract_app_name(title)
return
def handle_termination(self):
self.handle_change("")
self.__interval.dump_interval()
self.__interval.db.print_db()
return
\ No newline at end of file
from tkinter import *
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from plotter import ActivityPlotter
from datetime import datetime
from tkinter.ttk import *
from tkcalendar import DateEntry
today = datetime.now()
plotter = ActivityPlotter()
figure1, figure2, figure3 = plotter.get_figs(today)
root = Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
root.geometry("%dx%d"%(width,height))
l1 = Label(root, text = "Activity tracker for today")
l2 = Label(root, text = "total time spent")
# grid method to arrange labels in respective
# rows and columns as specified
l1.grid(row = 0, column = 0, sticky = 'W', pady = 2)
l2.grid(row = 0, column = 1, sticky = 'W', pady = 2)
cal = DateEntry(root, year=today.year, month = today.month, day=today.day)
cal.grid(row=1, column=0, columnspan=2, sticky='W')
weekly = FigureCanvasTkAgg(figure1, root)
weekly.get_tk_widget().grid(row=2, column=0, columnspan=2, sticky='N')
daily = FigureCanvasTkAgg(figure2, root)
daily.get_tk_widget().grid(row=3, column=0, columnspan=2, sticky='N')
perapp = FigureCanvasTkAgg(figure3, root)
perapp.get_tk_widget().grid(row=1, column=2, rowspan=3, sticky= 'E')
root.mainloop()
\ No newline at end of file
perapp.png

13.3 KB

from utils import *
from dbhandler import DBHandler
import numpy as np
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
class ActivityPlotter:
def __init__(self) -> None:
config = load_config()
self.db = DBHandler(config['DATABASE_FILE'])
self.week_days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
#self.curr_week = datetime.fromtimestamp(0)
def __init_dataholders(self):
self.perday = np.zeros(7)
self.perhour = np.zeros(24)
self.perapp = dict()
return
def __load_weekly_data(self):
self.week_data = self.db.read_week_data(self.curr_date)
self.__init_dataholders()
for event in self.week_data:
event_timestamp = datetime.fromtimestamp(event[0])
event_date = event_timestamp.date()
event_day = event_date.weekday()
if event_date == self.curr_date.date():
self.perhour[event_timestamp.hour] += event[2]
if event[1] in self.perapp:
self.perapp[event[1]] += event[2]
else:
self.perapp[event[1]] = event[2]
self.perday[event_day] += event[2]
self.perapp = dict(sorted(self.perapp.items(), key=lambda x:x[1],reverse=True))
def __get_week_plot(self):
figure = plt.Figure(figsize=(10, 5), dpi=70)
ax = figure.add_subplot(111)
ax.bar(self.week_days, self.perday, width = 0.35)
ax.set_xlabel("Week Days")
ax.set_ylabel(f"Total Screen Time in min")
ax.set_title("Total daily screen time in a week")
return figure
def __get_day_plot(self):
figure = plt.Figure(figsize=(10, 5), dpi=70)
ax = figure.add_subplot(111)
ax.bar(np.arange(24), self.perhour, width = 0.35)
ax.set_xlabel("Hour of the day")
ax.set_ylabel(f"Total Screen Time in sec")
ax.set_title("Total hourly screen time in a day")
figure.savefig('daily.png')
return figure
def __get_app_plot(self):
figure = plt.Figure(figsize=(6, 10), dpi=70)
ax = figure.add_subplot(111)
ax.barh(list(self.perapp.keys())[::-1], list(self.perapp.values())[::-1])
ax.set_xlabel("Time spent on the application")
ax.set_ylabel("Applications")
ax.set_title("Activity per Application")
figure.savefig('perapp.png')
return figure
def get_figs(self, timestamp:datetime):
self.curr_date = timestamp
self.__load_weekly_data()
week_fig = self.__get_week_plot()
day_fig = self.__get_day_plot()
app_fig = self.__get_app_plot()
return week_fig, day_fig, app_fig
"""
plotter = ActivityPlotter()
fig1, fig2, fig3 = plotter.get_figs(datetime.now())
fig1.savefig('weekly.png')
fig2.savefig('daily.png')
fig3.savefig('perapp.png')
"""
\ No newline at end of file
from dbhandler import *
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import numpy as np
db = DBHandler('data/activity_data.db')
today = datetime.today()
data = db.read_week_data(date=today)
start = today - timedelta(days=today.weekday())
weekly_sum = np.zeros(7)
daily_sum = np.zeros(24)
app_time = dict()
week_ind = np.arange(7)
for entry in data:
timestamp = datetime.fromtimestamp(entry[0])
currdate = timestamp.date()
day = currdate.weekday()
if currdate == today.date():
daily_sum[timestamp.hour] += entry[2]
if entry[1] in app_time:
app_time[entry[1]] += entry[2]
else:
app_time[entry[1]] = entry[2]
weekly_sum[day] += entry[2]
print(weekly_sum)
max = np.max(weekly_sum)
print(max)
time_format = "min"
app_time = dict(sorted(app_time.items(), key=lambda x:x[1],reverse=True))
max_app_time = list(app_time.items())[0][1]
print(max_app_time)
if max > 60*60:
time_format = "hr"
weekly_sum /= 3600
else:
weekly_sum /= 60
print(daily_sum)
figure1 = plt.Figure(figsize=(6, 5), dpi=100)
ax1 = figure1.add_subplot(111)
#bar1 = FigureCanvasTkAgg(figure1, root)
#bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
ax1.bar(week_day, weekly_sum, width = 0.35)
ax1.set_xlabel("Week Days")
ax1.set_ylabel(f"Total Screen Time in {time_format}")
ax1.set_title("Total daily screen time in a week")
figure1.savefig('weekly.png')
figure2 = plt.Figure(figsize=(6, 5), dpi=100)
ax2 = figure2.add_subplot(111)
#bar1 = FigureCanvasTkAgg(figure1, root)
#bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
ax2.bar(np.arange(24), daily_sum, width = 0.35)
ax2.set_xlabel("Hour of the day")
ax2.set_ylabel(f"Total Screen Time in sec")
ax2.set_title("Total hourly screen time in a day")
figure2.savefig('daily.png')
figure3 = plt.Figure(figsize=(6, 5), dpi=100)
ax3 = figure3.add_subplot(111)
#bar1 = FigureCanvasTkAgg(figure1, root)
#bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
print(list(app_time.keys()))
print(list(app_time.values()))
ax3.barh(list(app_time.keys())[::-1], list(app_time.values())[::-1])
ax3.set_xlabel("Time spent on the application")
ax3.set_ylabel("Applications")
ax3.set_title("Activity per Application")
figure3.savefig('perapp.png')
exit()
app_set = set()
for entry in data:
if entry[1] not in app_set:
app_set.add(entry[1])
week_data = np.zeros(len(app_set), 8)
week_data = week_data.sort(key=lambda item: item[7], reverse = True)
ind = np.arange(7)
width = 0.35
fig = plt.subplots(figsize =(10, 7))
prev_sum = np.zeros(0,7)
for i in len(week_data):
plt.bar(ind, week_data[i][0:7], width, bottom = prev_sum)
prev_sum += week_data[i][0:7]
plt.ylabel('Screen Time')
plt.title('Dates')
plt.xticks(ind, )
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('boys', 'girls'))
plt.show()
plt.plot([0,1,2], [2,3,4])
plt.legend()
\ No newline at end of file
from utils import *
from contextlib import contextmanager
from typing import Any, Dict, Optional, Tuple, Union # noqa
import os
from eventhandler import EventHandler
from Xlib import X
from Xlib.display import Display
from Xlib.error import XError
from Xlib.xobject.drawable import Window
from Xlib.protocol.rq import Event
import signal
import sys
win_title = ""
# Connect to the X server and get the root window
disp = Display()
root = disp.screen().root
# Prepare the property names we use so they can be fed into X11 APIs
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME') # UTF-8
WM_NAME = disp.intern_atom('WM_NAME') # Legacy encoding
last_seen = {'xid': None, 'title': None} # type: Dict[str, Any]
@contextmanager
def window_obj(win_id: Optional[int]) -> Window:
window_obj = None
if win_id:
try:
window_obj = disp.create_resource_object('window', win_id)
except XError:
pass
yield window_obj
def get_active_window() -> Tuple[Optional[int], bool]:
response = root.get_full_property(NET_ACTIVE_WINDOW,
X.AnyPropertyType)
if not response:
return None, False
win_id = response.value[0]
focus_changed = (win_id != last_seen['xid'])
if focus_changed:
with window_obj(last_seen['xid']) as old_win:
if old_win:
old_win.change_attributes(event_mask=X.NoEventMask)
last_seen['xid'] = win_id
with window_obj(win_id) as new_win:
if new_win:
new_win.change_attributes(event_mask=X.PropertyChangeMask)
return win_id, focus_changed
def _get_window_name_inner(win_obj: Window) -> str:
for atom in (NET_WM_NAME, WM_NAME):
try:
window_name = win_obj.get_full_property(atom, 0)
except UnicodeDecodeError:
title = "<could not decode characters>"
else:
if window_name:
win_name = window_name.value
if isinstance(win_name, bytes):
win_name = win_name.decode('latin1', 'replace')
return win_name
else:
title = "<unnamed window>"
return "{} (XID: {})".format(title, win_obj.id)
def get_window_name(win_id: Optional[int]) -> Tuple[Optional[str], bool]:
"""Look up the window name for a given X11 window ID"""
if not win_id:
last_seen['title'] = None
return last_seen['title'], True
title_changed = False
with window_obj(win_id) as wobj:
if wobj:
try:
win_title = _get_window_name_inner(wobj)
except XError:
pass
else:
title_changed = (win_title != last_seen['title'])
last_seen['title'] = win_title
return last_seen['title'], title_changed
def handle_xevent(event_handler, event):
if event.type != X.PropertyNotify:
return
changed = False
if event.atom == NET_ACTIVE_WINDOW:
if get_active_window()[1]:
get_window_name(last_seen['xid'])
changed = True
elif event.atom in (NET_WM_NAME, WM_NAME):
changed = changed or get_window_name(last_seen['xid'])[1]
if changed:
handle_change(event_handler, last_seen)
def handle_change(event_handler, new_state: dict):
event_handler.handle_change(new_state['title'])
return
def signal_handler(sig, frame):
print('Got signal', sig)
event_handler.handle_termination()
sys.exit(0)
def init_eventhandler():
config = load_config()
event_handler = EventHandler(config["INTERVAL"], config["DATABASE_FILE"])
return event_handler
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
print("Current deamon pid:", os.getpid())
event_handler = init_eventhandler()
root.change_attributes(event_mask=X.PropertyChangeMask)
get_window_name(get_active_window()[0])
handle_change(event_handler, last_seen)
while True:
handle_xevent(event_handler, disp.next_event())
\ No newline at end of file
from curses.ascii import isalnum
import json
def extract_app_name(title):
if not title:
return ""
full_title_list = title.split()
i = len(full_title_list)-1
app_name = ""
while i >= 0 and len(full_title_list[i]) != 1 and full_title_list[i].isalnum():
app_name = full_title_list[i] + " " + app_name
i -= 1
return app_name
def load_config():
f = open('config.json')
config = json.load(f)
f.close()
return config
\ No newline at end of file
weekly.png

19.5 KB

Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment