Last active
June 3, 2021 13:46
-
-
Save ZoomTen/f46a167eeb04377640623abc5813697f to your computer and use it in GitHub Desktop.
Activity logger that I run every night before I go to bed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
from appdirs import AppDirs | |
from argparse import ArgumentParser | |
from configparser import ConfigParser | |
from datetime import datetime, date | |
from textwrap import TextWrapper as tw | |
import os | |
import sys | |
import sqlite3 | |
import shutil | |
import re | |
appname = "PyActivityLog" | |
appversion = "1.3.2" | |
app_displayname = "Zumi's Activity Manager" | |
def prompt_yn(prompt, default_yes=False): | |
y = ['yes', 'y', 'yeah', 'yea', 'ye', 'ya'] | |
n = ['no', 'nah', 'n', 'nein'] | |
if default_yes: | |
prompt = prompt + " [Y/n] " | |
else: | |
prompt = prompt + " [y/N] " | |
choice = input(prompt).lower() | |
if choice in y: | |
return True | |
elif choice in n: | |
return False | |
elif choice == '': | |
if default_yes: | |
return True | |
else: | |
return False | |
else: | |
print("Choose yes/y or no/n") | |
def eprint(*args, **kwargs): | |
return None #print(*args, file=sys.stderr, **kwargs) | |
class ActivityLog(): | |
def __init__(self): | |
self.commands = { | |
"checkdate": self.checkdate, | |
"setdate": self.setdate, | |
"log": self.push, | |
"dump": self.dump, | |
"dumptoday": self.dumpdate, | |
"todo": self.addtodo, | |
"todolist": self.listtodo, | |
"list": self.list, | |
"listtoday": self.listtoday, | |
"peek": self.peek, | |
"addproject": self.addproject, | |
"subproject": self.addsubproject, | |
"projects": self.listprojects, | |
"info": self.info, | |
"dumpstats": self.dumpstats | |
} | |
dirs = AppDirs(appname) | |
self.config = dirs.user_config_dir | |
if not os.path.exists(self.config): | |
if prompt_yn("Config dir: {} doesn't exist. Create?".format(self.config), default_yes=True): | |
os.mkdir(self.config) | |
print("Created config dir.") | |
else: | |
print("Can't continue without config dir, exiting.") | |
exit(0) | |
self.conffile = os.path.join(self.config, 'config.ini') | |
self.confini = ConfigParser() | |
# fallback is 80 cols / 25 lines | |
self.term_size = shutil.get_terminal_size((72,25)) | |
# TODO: Currently hardcoded config path | |
if not os.path.exists(self.conffile): | |
# Create defaults | |
self.data = dirs.user_data_dir | |
self.confini['Settings'] = {'Date':'today', 'DataPath':self.data} | |
with open(self.conffile, 'w') as c: | |
self.confini.write(c) | |
else: | |
self.confini.read(self.conffile) | |
if self.confini['Settings']['DataPath']: | |
self.data = self.confini['Settings']['DataPath'] | |
else: | |
self.data = dirs.user_data_dir | |
if not os.path.exists(self.data): | |
if prompt_yn("Data dir: {} doesn't exist. Create?".format(self.data), default_yes=True): | |
os.mkdir(self.data) | |
print("Created data dir.") | |
else: | |
print("Can't continue without data dir, exiting.") | |
exit(0) | |
self.datafile = os.path.join(self.data, 'activity.db') | |
self.db = sqlite3.connect(self.datafile) | |
if self.db.execute(''' | |
Select Count(name) From sqlite_master | |
Where type='table' And name='activities' | |
''').fetchone()[0] == 0: | |
self.db.execute(''' | |
Create Table activities( | |
date Text Not Null, | |
time Text Not Null, | |
activity Text Not Null | |
); | |
''') | |
eprint("Created activities table") | |
if self.db.execute(''' | |
Select Count(name) From sqlite_master | |
Where type='table' And name='todos' | |
''').fetchone()[0] == 0: | |
self.db.execute(''' | |
Create Table todos( | |
at_date Text, | |
at_time Text, | |
activity Text Not Null, | |
done Int Not Null Default 0, | |
completed_on Text, | |
done_by Int | |
); | |
''') | |
eprint("Created todos table") | |
if self.db.execute(''' | |
Select Count(name) From sqlite_master | |
Where type='table' And name='projects' | |
''').fetchone()[0] == 0: | |
self.db.execute(''' | |
Create Table projects( | |
added_on Text, | |
target_date Text, | |
project_title Text, | |
project_desc Text, | |
done Int Not Null Default 0, | |
done_by Int | |
); | |
''') | |
eprint("Created projects table") | |
if self.db.execute(''' | |
Select Count(name) From sqlite_master | |
Where type='table' And name='subprojects' | |
''').fetchone()[0] == 0: | |
self.db.execute(''' | |
Create Table subprojects( | |
added_on Text, | |
target_date Text, | |
subproject Text, | |
done Int Not Null Default 0, | |
belongs_to Int Not Null Default 0, | |
subproject_index Int Not Null, | |
done_by Int | |
); | |
''') | |
eprint("Created subprojects table") | |
if self.confini['Settings']['Date'].lower() == 'today': | |
self.thedate = date.today().isoformat() | |
else: | |
try: | |
self.thedate = date.fromisoformat(self.confini['Settings']['Date']) | |
except ValueError as e: | |
eprint("Error in config file {}: Date must be 'today' or a date in ISO format. ({})".format(self.conffile, e)) | |
eprint("Defaulting to 'today'.") | |
self.thedate = date.today().isoformat() | |
# activitylog decorator | |
def al_command(func): | |
def wrapper(self, *args, **kwargs): | |
eprint("{} version {}".format(app_displayname, appversion)) | |
#eprint("action: {}".format(func.__name__)) | |
eprint("-"*10) | |
eprint("Using config : {}".format(self.conffile)) | |
eprint("Using data : {}".format(self.datafile)) | |
eprint("-"*10) | |
eprint() | |
func(self, *args, **kwargs) | |
return self.db.close() | |
return wrapper | |
def register_subparsers(self, sp): | |
for name, command in self.commands.items(): | |
if command == self.checkdate: | |
ap = sp.add_parser(name, help="Check the current log date", description="Check the current log date.") | |
if command == self.setdate: | |
ap = sp.add_parser(name, help="Set the current log date", description="Set the current log date.") | |
ap.add_argument('entry_date') | |
if command == self.push: | |
ap = sp.add_parser(name, help="Log an activity entry", description="Log an activity entry. Use [todo#XXX] to mark a completed to-do entry. Use [proj#XXX.XXX] to mark a subproject as complete. Use [proj#XXX] to mark a project as complete.") | |
ap.add_argument('entry_string') | |
ap.add_argument('--date', '-d', help="Date for the entry") | |
ap.add_argument('--time', '-t', help="Time for the entry") | |
if command == self.dump: | |
ap = sp.add_parser(name, help="Dump the activity database", description="Dump the activity database into CSV formatted output.") | |
if command == self.dumpdate: | |
ap = sp.add_parser(name, help="Dump the activity database for the day", description="Dump the activity database for the specified day into CSV formatted output.") | |
if command == self.list: | |
ap = sp.add_parser(name, help="List every logged activity", description="List every logged activity as neatly formatted output.") | |
ap.add_argument('--count', '-c', help="Count how many activities are there for the day", action='store_true') | |
if command == self.listtoday: | |
ap = sp.add_parser(name, help="List every activity for the day", description="List every logged activity as neatly formatted output for the specified day.") | |
ap.add_argument('--count', '-c', help="Count how many activities are there for the day", action='store_true') | |
if command == self.addtodo: | |
ap = sp.add_parser(name, help="Add a to-do entry", description="Add a to-do entry.") | |
ap.add_argument('entry_string') | |
ap.add_argument('--on', '-d', help="Date for the specified to-do") | |
ap.add_argument('--at', '-t', help="Time for the specified to-do") | |
if command == self.listtodo: | |
ap = sp.add_parser(name, help="List to-dos", description="Neatly format a to-do list.") | |
ap.add_argument('--all', '-a', help="Include entries marked as done", action='store_true') | |
ap.add_argument('--num', '-n', help="View one to-do. Overrides --all.", type=int) | |
ap.add_argument('--left', '-l', help="View to-do's left to be done. Overrides --all.", action='store_true') | |
if command == self.peek: | |
ap = sp.add_parser(name, help="Look at a specified activity ID", description="Look at a specified activity ID, useful to see which activity was referred by todolist -a.") | |
ap.add_argument('id') | |
if command == self.addproject: | |
ap = sp.add_parser(name, help="Add a project", description="Add a project.") | |
ap.add_argument('project_string') | |
ap.add_argument('--by', '-d', help="Target overall completion date for the project") | |
ap.add_argument('--desc', '-e', help="Describe the project") | |
if command == self.addsubproject: | |
ap = sp.add_parser(name, help="Add a subproject / task to the project", description="Add a subproject / task to the project.") | |
ap.add_argument('project_id', type=int) | |
ap.add_argument('subproject_string') | |
ap.add_argument('--by', '-d', help="Target overall completion date for the subproject") | |
if command == self.listprojects: | |
ap = sp.add_parser(name, help="List current projects", description="List current projects as neatly formatted output.") | |
ap.add_argument('--all', '-a', help="Include projects marked as done", action='store_true') | |
ap.add_argument('--num', '-n', help="View one project. Overrides --all.", type=int) | |
if command == self.info: | |
ap = sp.add_parser(name, help="View stats", description="View information and stats about your activities.") | |
if command == self.dumpstats: | |
ap = sp.add_parser(name, help="Dump the activity counter", description="Dump the activity counter for each day as CSV-formatted output.") | |
@al_command | |
def info(self, args): | |
total = self.db.execute(''' | |
Select Count(*) From activities; | |
''').fetchone()[0] | |
day_total = self.db.execute(''' | |
Select Count(*) From activities | |
Where date = ?; | |
''',(self.thedate,)).fetchone()[0] | |
day_high = self.db.execute(''' | |
Select date, Max(count) From ( | |
Select date As date, Count(*) as count | |
From activities | |
Group By date | |
); | |
''').fetchone() | |
day_low = self.db.execute(''' | |
Select date, Min(count) From ( | |
Select date As date, Count(*) as count | |
From activities | |
Group By date | |
); | |
''').fetchone() | |
day_avg = self.db.execute(''' | |
Select Avg(count) From ( | |
Select date As date, Count(*) as count | |
From activities | |
Group By date | |
); | |
''').fetchone()[0] | |
todo_done = self.db.execute(''' | |
Select Count(*) From todos | |
Where done_by Not Null; | |
''').fetchone()[0] | |
project_done = self.db.execute(''' | |
Select Count(*) From projects | |
Where done_by Not Null; | |
''').fetchone()[0] | |
subproject_done = self.db.execute(''' | |
Select Count(*) From subprojects | |
Where done_by Not Null; | |
''').fetchone()[0] | |
print("Total activities logged so far : {}".format(total)) | |
print("Total activities logged for today : {}".format(day_total)) | |
print("---") | |
print("Most productive day : {} ({} activities)".format(day_high[0], day_high[1])) | |
print("Least productive day : {} ({} activities)".format(day_low[0], day_low[1])) | |
print("---") | |
if day_avg: | |
print("Average \"productivity\" : {:.2f}".format(day_avg)) | |
print("---") | |
print("Completed todos : {}".format(todo_done)) | |
print("Completed projects : {}".format(project_done)) | |
print("Completed subprojects : {}".format(subproject_done)) | |
@al_command | |
def dumpstats(self, args): | |
eprint("CSV dump of per-day activity count") | |
query = self.db.execute(''' | |
Select date As date, Count(*) As count From activities | |
Group By date; | |
''') | |
self.csv_dump_common(query) | |
@al_command | |
def checkdate(self, args): | |
print("Currently set date is {} ({})".format(self.thedate, self.confini['Settings']['Date'])) | |
@al_command | |
def setdate(self, args): | |
date_input = args.entry_date.strip() | |
if date_input.lower() == 'today': | |
self.confini['Settings']['Date'] = date_input | |
self.confini['Settings']['Date'] = date_input | |
else: | |
try: | |
isodate = date.fromisoformat(date_input) | |
except ValueError as e: | |
eprint("Error: Date must be 'today' or a date in ISO format ({})".format(e)) | |
exit(1) | |
finally: | |
self.confini['Settings']['Date'] = date_input | |
with open(self.conffile, 'w') as c: | |
self.confini.write(c) | |
print("Date successfully changed to {}".format(date_input)) | |
@al_command | |
def push(self, args): | |
if args.date and args.time: | |
d = args.date | |
t = args.time | |
else: | |
d = self.thedate | |
t = datetime.now().time().isoformat('seconds') | |
i = self.db.execute(''' | |
Insert Into activities(date, time, activity) | |
Values (?, ?, ?); | |
''', (d, t, args.entry_string)) | |
r = i.lastrowid | |
print("Logged activity [{} {}]:".format(d, t)) | |
has_todo = re.findall(r'\[todo#(\d+)\]', args.entry_string) | |
a_str = re.sub(r'\[todo#(\d+)\]', '', args.entry_string).strip() | |
has_proj = re.findall(r'\[proj#(\d+).?(\d+)?\]', a_str) | |
a_str = re.sub(r'\[proj#(\d+).?(\d+)?\]', '', a_str).strip() | |
wrap_act = tw(width=self.term_size.columns, initial_indent=' '*4,subsequent_indent=' '*4) | |
print(wrap_act.fill('\033[1m{}\033[0m'.format(a_str))) | |
if has_todo: | |
for todo_id in has_todo: | |
todo_id = int(todo_id) | |
if self.db.execute('Select 1 From todos Where rowid=?',(todo_id,)).fetchone(): | |
self.db.execute(''' | |
Update todos | |
Set done = 1, completed_on = ?, done_by = ? | |
Where rowid = ? | |
''', ("{} {}".format(d,t), i.lastrowid, todo_id)) | |
print(wrap_act.fill('[Completed todo #{}]'.format(todo_id))) | |
if has_proj: | |
for proj, subproj in has_proj: | |
proj_id = int(proj) | |
if subproj == '': | |
if self.db.execute('Select 1 From projects Where rowid=?',(proj_id,)).fetchone(): | |
self.db.execute(''' | |
Update projects | |
Set done = 1, done_by = ? | |
Where rowid = ? | |
''', (i.lastrowid, proj_id)) | |
print(wrap_act.fill('[Completed project #{}]'.format(proj_id))) | |
else: | |
subproj_id = int(subproj) | |
if self.db.execute('Select 1 From subprojects Where belongs_to=? And subproject_index=?',(proj_id,subproj_id,)).fetchone(): | |
self.db.execute(''' | |
Update subprojects | |
Set done = 1, done_by = ? | |
Where belongs_to=? And subproject_index=? | |
''', (i.lastrowid, proj_id, subproj_id)) | |
print(wrap_act.fill('[Completed subproject #{}.{}]'.format(proj_id, subproj_id))) | |
self.db.commit() | |
def vali_date(self, date_string): | |
date_input = date_string.strip() | |
try: | |
isodate = date.fromisoformat(date_input) | |
except ValueError as e: | |
eprint("Error: Date must be a date in ISO format ({})".format(e)) | |
exit(1) | |
finally: | |
return date_input | |
@al_command | |
def addtodo(self, args): | |
d = None | |
t = None | |
if args.on: | |
d = self.vali_date(args.on) | |
if args.at: | |
time_input = args.at.strip() | |
if re.match(r'[012][0-9]:\d{2}', time_input): | |
t = time_input | |
else: | |
eprint("Error: Time must be in HH:MM format") | |
exit(1) | |
i = self.db.execute(''' | |
Insert Into todos(at_date, at_time, activity) | |
Values (?, ?, ?); | |
''', (d, t, args.entry_string)) | |
if d is None: | |
d = "no date" | |
if t is None: | |
t = "no time" | |
r = i.lastrowid | |
wrap_act = tw(width=self.term_size.columns, initial_indent=' '*4,subsequent_indent=' '*4) | |
print("Created todo {}:".format(i.lastrowid)) | |
print(wrap_act.fill('\033[1m{}\033[0m'.format(args.entry_string))) | |
print(wrap_act.fill('(due {} at {})'.format(d,t))) | |
self.db.commit() | |
@al_command | |
def listtodo(self, args): | |
eprint("Todo list") | |
if args.num: | |
query = self.db.execute(''' | |
Select rowid, * From todos | |
Where rowid=?; | |
''',(args.num,)) | |
elif args.left: | |
query = self.db.execute(''' | |
Select Count(rowid) From todos | |
Where done=0; | |
''') | |
elif args.all: | |
query = self.db.execute(''' | |
Select rowid, * From todos | |
Order By at_date Asc, at_time Asc; | |
''') | |
else: | |
query = self.db.execute(''' | |
Select rowid, * From todos | |
Where done=0 | |
Order By at_date Asc, at_time Asc; | |
''') | |
if args.left: | |
print(list(query)[0][0]) | |
else: | |
for i in list(query): | |
row_id = i[0] | |
at_date = i[1] | |
at_time = i[2] | |
activity = i[3] | |
done = i[4] | |
completed_on = i[5] | |
done_by = i[6] | |
if args.all: | |
if done > 0: | |
done_string = "[X]" | |
else: | |
done_string = "[ ]" | |
else: | |
done_string = "-" | |
if at_date: | |
due_string = "(due {} at --:--)".format(at_date) | |
if at_time: | |
due_string = "(due {} at {})".format(at_date, at_time) | |
elif at_time: | |
due_string = "(due ---------- at {})".format(at_time) | |
else: | |
due_string = "------------------------ " | |
if completed_on: | |
complete_string = "(completed on {})".format(completed_on) | |
if done_by: | |
complete_string = "(completed on {} by activity #{})".format(completed_on, done_by) | |
else: | |
complete_string = "" | |
prefix = "{} #{:<4} ".format(done_string, row_id) | |
wrap_mes = tw(width=self.term_size.columns, initial_indent=prefix, subsequent_indent=' '*len(prefix)) | |
wrap_due = tw(width=self.term_size.columns, initial_indent=' '*(len(prefix)+4), subsequent_indent=' '*(len(prefix)+4)) | |
print(wrap_mes.fill('\033[1m{}\033[0m'.format(activity))) | |
print(wrap_due.fill(due_string)) | |
print(wrap_due.fill(complete_string)) | |
if completed_on: print() | |
def csv_dump_common(self, query): | |
# used by dumptoday and dump | |
for i in query.description: | |
print(i[0], end=';') | |
print() | |
for i in list(query): | |
for j in i: | |
print(j, end=';') | |
print() | |
@al_command | |
def dumpdate(self, args): | |
eprint("CSV dump of activities on {}".format(self.thedate)) | |
query = self.db.execute(''' | |
Select * From activities | |
Where date=? | |
Order By time; | |
''', (self.thedate,)) | |
self.csv_dump_common(query) | |
@al_command | |
def dump(self, args): | |
eprint("CSV dump of all activities") | |
query = self.db.execute(''' | |
Select * From activities | |
Order By date, time; | |
''') | |
self.csv_dump_common(query) | |
def list_common(self, query_entry): | |
# used by listtoday and list | |
a_num = query_entry[0] | |
a_date = query_entry[1] | |
a_time = query_entry[2] | |
a_has_todo = re.findall(r'\[todo#(\d+)\]', query_entry[3]) | |
a_has_proj = re.findall(r'\[proj#(\d+).?(\d+)?\]', query_entry[3]) | |
a_str = re.sub(r'\[todo#(\d+)\]', '', query_entry[3]).strip() | |
a_str = re.sub(r'\[proj#(\d+).?(\d+)?\]', '', a_str).strip() | |
prefix = "{:>5}> ({} {}) ".format(a_num, a_date, a_time) | |
wrap_act = tw(width=self.term_size.columns, initial_indent=prefix, subsequent_indent=' '*len(prefix)) | |
print(wrap_act.fill('\033[1m{}\033[0m'.format(a_str))) | |
if a_has_todo: | |
for todo in a_has_todo: | |
wrap_todo = tw(width=self.term_size.columns, initial_indent=' '*29, subsequent_indent=' '*29) | |
print(wrap_todo.fill('[Completes todo #{}]'.format(todo))) | |
if a_has_proj: | |
for proj, subproj in a_has_proj: | |
wrap_proj = tw(width=self.term_size.columns, initial_indent=' '*29, subsequent_indent=' '*29) | |
if subproj == '': | |
print(wrap_proj.fill('[Completes project #{}]'.format(proj))) | |
else: | |
print(wrap_proj.fill('[Completes subproject #{}.{}]'.format(proj, subproj))) | |
@al_command | |
def listtoday(self, args): | |
query = self.db.execute(''' | |
Select rowid, * From activities | |
Where date=? | |
Order By time; | |
''', (self.thedate,)) | |
activities = list(query) | |
if args.count: | |
print(len(activities)) | |
else: | |
print("\033[1mList of activities on {}\033[0m".format(self.thedate)) | |
for i in activities: | |
self.list_common(i) | |
@al_command | |
def list(self, args): | |
query = self.db.execute(''' | |
Select rowid, * From activities | |
Order By date, time; | |
''') | |
activities = list(query) | |
if args.count: | |
print(len(activities)) | |
else: | |
print("\033[1mList of all activities\033[0m") | |
for i in activities: | |
self.list_common(i) | |
@al_command | |
def peek(self, args): | |
query = self.db.execute(''' | |
Select rowid, * From activities | |
Where rowid=?; | |
''',(args.id,)) | |
peek_result = query.fetchone() | |
if peek_result: | |
a_num = peek_result[0] | |
a_date = peek_result[1] | |
a_time = peek_result[2] | |
a_has_todo = re.search(r'\[todo#(\d+)\]', peek_result[3]) | |
a_str = re.sub(r'\[todo#(\d+)\]', '', peek_result[3]).strip() | |
print("Activity {} [{} {}]:".format(a_num, a_date, a_time)) | |
wrap_act = tw(width=self.term_size.columns, initial_indent=' '*4,subsequent_indent=' '*4) | |
print(wrap_act.fill('\033[1m{}\033[0m'.format(a_str))) | |
if a_has_todo: | |
print(wrap_act.fill('[Completes todo #{}]'.format(a_has_todo.groups()[0]))) | |
else: | |
print('Activity {} not found'.format(args.id)) | |
@al_command | |
def addproject(self, args): | |
by = None | |
if args.by: | |
by = self.vali_date(args.by) | |
desc = None | |
if args.desc: | |
desc = args.desc | |
d = self.thedate | |
t = datetime.now().time().isoformat('seconds') | |
i = self.db.execute(''' | |
Insert Into projects(added_on, target_date, project_title, project_desc) | |
Values (?, ?, ?, ?); | |
''', ('{} {}'.format(d,t), by, args.project_string, desc)) | |
r = i.lastrowid | |
print("Created project {} [{} {}]".format(r, d, t)) | |
if by: | |
print("(Due by {})".format(by)) | |
wrap_act = tw(width=self.term_size.columns, initial_indent=' '*4,subsequent_indent=' '*4) | |
print(wrap_act.fill('\033[1m{}\033[0m'.format(args.project_string))) | |
if desc: | |
wrap_desc = tw(width=self.term_size.columns, initial_indent=' '*6,subsequent_indent=' '*6) | |
print(wrap_desc.fill('{}'.format(desc))) | |
self.db.commit() | |
@al_command | |
def addsubproject(self, args): | |
d = self.thedate | |
t = datetime.now().time().isoformat('seconds') | |
by = None | |
if args.by: | |
by = self.vali_date(args.by) | |
get_project_id = self.db.execute(''' | |
Select rowid From projects | |
Where rowid=?; | |
''',(args.project_id,)) | |
project_id_valid = get_project_id.fetchone() | |
if project_id_valid: | |
get_last_subproject_id = self.db.execute(''' | |
Select Max(subproject_index) From subprojects | |
Where belongs_to=?; | |
''',(args.project_id,)) | |
last_subproject_id = get_last_subproject_id.fetchone()[0] | |
if last_subproject_id is not None: | |
current_index = int(last_subproject_id) + 1 | |
else: | |
current_index = 0 | |
i = self.db.execute(''' | |
Insert Into subprojects(added_on, target_date, subproject, belongs_to, subproject_index) | |
Values (?, ?, ?, ?, ?); | |
''', ('{} {}'.format(d,t), by, args.subproject_string, args.project_id, current_index)) | |
print("Created subproject {}.{} [{} {}]".format(args.project_id, current_index, d, t)) | |
if by: | |
print("(Due by {})".format(by)) | |
wrap_act = tw(width=self.term_size.columns, initial_indent=' '*4,subsequent_indent=' '*4) | |
print(wrap_act.fill('\033[1m{}\033[0m'.format(args.subproject_string))) | |
self.db.commit() | |
else: | |
print("Can't find project {} -- project_id must be valid".format(args.project_id)) | |
@al_command | |
def listprojects(self, args): | |
query_string = 'Select rowid, * From projects ' | |
if args.num: | |
query_string += 'Where rowid=?;' | |
projects_list = self.db.execute(query_string, (args.num,)) | |
elif args.all: | |
query_string += ';' | |
projects_list = self.db.execute(query_string) | |
else: | |
query_string += 'Where done=0;' | |
projects_list = self.db.execute(query_string) | |
for proj_id, added, target, title, desc, done, done_by in projects_list: | |
if args.all or args.num: | |
if done: | |
done_string = "[x]" | |
else: | |
done_string = "[ ]" | |
else: | |
done_string = "-" | |
complete_string = "" | |
prefix = "{} #{:<4} ".format(done_string, proj_id) | |
wrap_mes = tw(width=self.term_size.columns, initial_indent=prefix, subsequent_indent=' '*len(prefix)) | |
wrap_desc = tw(width=self.term_size.columns, initial_indent=' '*len(prefix), subsequent_indent=' '*len(prefix)) | |
print(wrap_mes.fill('\033[1m{}\033[0m'.format(title))) | |
if done_by: | |
print(wrap_desc.fill("(completed by activity #{})".format(done_by))) | |
if target: | |
print(wrap_desc.fill('\033[3m(Due by {})\033[0m'.format(target))) | |
if desc: | |
print() | |
print(wrap_desc.fill('{}'.format(desc))) | |
subprojects = self.db.execute(''' | |
Select * From subprojects | |
Where belongs_to = ?; | |
''', (proj_id,)) | |
subproject_list = [] | |
first_subproject = subprojects.fetchone() | |
subproject_list.append(first_subproject) | |
if first_subproject: | |
print() | |
print(wrap_desc.fill('==== Subprojects ====')) | |
for sp in subprojects.fetchall(): | |
subproject_list.append(sp) | |
for sp in subproject_list: | |
if sp: | |
added, target, subproject, done, belongs_to, index, done_by = sp | |
if done: | |
done_string = "[X]" | |
else: | |
done_string = "[ ]" | |
prefix = " {} #{:<7} ".format(done_string, '{}.{}'.format(belongs_to, index)) | |
wrap_sub = tw(width=self.term_size.columns, initial_indent=prefix, subsequent_indent=' '*len(prefix)) | |
wrap_due = tw(width=self.term_size.columns, initial_indent=' '*len(prefix), subsequent_indent=' '*len(prefix)) | |
if args.all: | |
print(wrap_sub.fill('\033[1m{}\033[0m'.format(subproject))) | |
if done_by: | |
print(wrap_due.fill("(completed by activity #{})".format(done_by))) | |
if target: | |
print(wrap_due.fill('\033[3m(due by {})\033[0m'.format(target))) | |
elif not done: | |
print(wrap_sub.fill('\033[1m{}\033[0m'.format(subproject))) | |
if done_by: | |
print(wrap_due.fill("(completed by activity #{})".format(done_by))) | |
if target: | |
print(wrap_due.fill('\033[3m(due by {})\033[0m'.format(target))) | |
print() | |
al = ActivityLog() | |
ap = ArgumentParser(description="Simple activity logger. Might help your productivity.") | |
sp = ap.add_subparsers(dest='action', help='Action to take', required=True) | |
al.register_subparsers(sp) | |
args = ap.parse_args() | |
al.commands[args.action](args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment