else/Toddo/toddo.py

315 lines
10 KiB
Python
Raw Normal View History

2015-08-03 04:28:40 +00:00
import datetime
import shutil
import sqlite3
import sys
import textwrap
SQL_ID = 0
SQL_TODOTABLE = 1
SQL_CREATED = 2
SQL_MESSAGE = 3
HELP_FULL = [
('> toddo', 'Display the todos from the current table'),
('> toddo all', 'Display the todos from all tables'),
('> toddo 4', 'Display the todo with ID 4'),
('> toddo add', 'Add a new todo via multi-line typing prompt'),
('> toddo add "message"', 'Add a new todo with this message'),
('> toddo remove 8', 'Remove the todo with ID 8'),
('> toddo table', 'Display the name of the current table'),
('> toddo table name', 'Switch to the table named "name"')
]
HELP_NOENTRIES = '''Your todo list is empty!
Use `toddo add` or `toddo add "message"` to make entries.'''
HELP_NOACTIVE = '''Table `%s` has no entries!
Use `toddo all` to see if there are entries for other tables.'''
HELP_REMOVE = '''Provide an ID number to remove.'''
class ToddoExc(Exception):
pass
class Toddo():
def __init__(self, dbname='C:/git/else/toddo/toddo.db'):
self.sql = sqlite3.connect(dbname)
self.cur = self.sql.cursor()
self.cur.execute('CREATE TABLE IF NOT EXISTS meta(key TEXT, val TEXT)')
self.cur.execute('CREATE TABLE IF NOT EXISTS todos(id INT, todotable TEXT, created INT, message TEXT)')
self.cur.execute('CREATE INDEX IF NOT EXISTS todoindex on todos(id)')
def add_todo(self, message=None):
'''
Create new entry in the database on the active todotable.
'''
if message is None:
message = multi_line_input()
message = str(message)
if message is '':
raise ToddoExc('Todos cannot be blank.')
todoid = self.increment_lastid()
todotable = self.get_todotable()
created = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
self.cur.execute('INSERT INTO todos VALUES(?, ?, ?, ?)', [todoid, todotable, created, message])
self.sql.commit()
return todoid
def remove_todo(self, idnumber):
'''
Drop todo from the database.
'''
idnumber = int(idnumber)
self.cur.execute('SELECT * FROM todos WHERE id=?', [idnumber])
todo = self.cur.fetchone()
if todo is None:
raise ToddoExc('Todo %d does not exist.' % idnumber)
activetable = self.get_todotable()
requestedtable = todo[SQL_TODOTABLE]
if requestedtable.lower() != activetable.lower():
raise ToddoExc('Todo %d is not part of the active table `%s`. It belongs to `%s`.' % (idnumber, activetable, requestedtable))
self.cur.execute('DELETE FROM todos WHERE id=?', [idnumber])
self.sql.commit()
return idnumber
def display_one_todo(self, idnumber):
'''
Make a nice display that shows a todo's entire contents.
'''
self.cur.execute('SELECT * FROM todos WHERE id=?', [idnumber])
todo = self.cur.fetchone()
if todo is None:
raise ToddoExc('Todo %d does not exist.' % idnumber)
message = todo[SQL_MESSAGE]
messageleft = len('Message: ')
width = shutil.get_terminal_size()[0] - (messageleft + 1)
message = nicewrap(message, width, messageleft)
return 'ID: %d\nTable: %s\nCreated: %s\nMessage: %s' % (
todo[SQL_ID], todo[SQL_TODOTABLE], human(todo[SQL_CREATED]), message)
def display_active_todos(self):
'''
Pass the active table name into display_todos_from_table
'''
todotable = self.get_todotable()
return self.display_todos_from_table(todotable)
def display_todos_from_table(self, todotable):
'''
Make a nice display from the database.
'''
if todotable is None:
self.cur.execute('SELECT * FROM todos ORDER BY id ASC')
else:
self.cur.execute('SELECT * FROM todos WHERE todotable=? ORDER BY id ASC', [todotable])
todos = self.cur.fetchall()
if len(todos) == 0:
return None
todos = [list(x) for x in todos]
longest_id = max(len(str(x[SQL_ID])) for x in todos)
longest_table = max(len(str(x[SQL_TODOTABLE])) for x in todos)
display = []
for todo in todos:
todoid = str(todo[SQL_ID])
todoid = (' '*(longest_id-len(todoid))) + todoid
timestamp = human(todo[SQL_CREATED])
todotable = todo[SQL_TODOTABLE]
todotable = (' '*(longest_table-len(todotable))) + todotable
message = todo[SQL_MESSAGE]
if '\n' in message:
message = message.split('\n')[0] + ' ...'
total = '%s : %s : %s : %s' % (todoid, todotable, timestamp, message)
terminal_width = shutil.get_terminal_size()[0]
if len(total) > terminal_width:
total = total[:(terminal_width-(len(total)+4))] + '...'
display.append(total)
return '\n'.join(display)
def switch_todotable(self, newtable=None):
'''
Update the meta with `newtable` as the new active todotable.
'''
self.cur.execute('SELECT val FROM meta WHERE key="todotable"')
activetable = self.cur.fetchone()
if not activetable:
activetable = self._install_default_todotable()
else:
activetable = activetable[0]
if newtable is None:
return activetable
self.cur.execute('UPDATE meta SET val=? WHERE key="todotable"', [newtable])
self.sql.commit()
return newtable
def increment_lastid(self, increment=False):
'''
Increment the lastid in the meta table, THEN return it.
'''
self.cur.execute('SELECT val FROM meta WHERE key="lastid"')
lastid = self.cur.fetchone()
if lastid is None:
self._install_default_lastid()
return 1
else:
lastid = int(lastid[0]) + 1
self.cur.execute('UPDATE meta SET val=? WHERE key="lastid"', [lastid])
return lastid
def get_todotable(self):
self.cur.execute('SELECT val FROM meta WHERE key="todotable"')
todotable = self.cur.fetchone()
if todotable is None:
self._install_default_todotable()
todotable = 'default'
else:
todotable = todotable[0]
return todotable
def _install_default_lastid(self):
'''
This method assumes that "lastid" does not already exist.
If it does, it's your fault for calling this.
'''
self.cur.execute('INSERT INTO meta VALUES("lastid", 1)')
self.sql.commit()
return 1
def _install_default_todotable(self):
'''
This method assumes that "todotable" does not already exist.
If it does, it's your fault for calling this.
'''
self.cur.execute('INSERT INTO meta VALUES("todotable", "default")')
self.sql.commit()
return 'default'
def human(timestamp):
timestamp = datetime.datetime.utcfromtimestamp(timestamp)
timestamp = datetime.datetime.strftime(timestamp, '%d %b %Y %H:%M')
return timestamp
def multi_line_input():
print('Submit a ctrl+z to finish typing.')
userinput = ''
ctrlz = '\x1a'
while True:
try:
additional = input('- ')
except EOFError:
# If you only enter a ctrlz
return userinput
if ctrlz in additional:
additional = additional.split(ctrlz)[0]
userinput += additional
break
userinput += additional + '\n'
return userinput.strip()
def nicewrap(message, width, paddingleft):
# http://stackoverflow.com/a/26538082 ##########################
message = '\n'.join(['\n'.join(textwrap.wrap(line, width,#######
break_long_words=True, replace_whitespace=False))##########
for line in message.split('\n')])##########################
################################################################
message = message.strip()
message = message.replace('\n', '\n' + (' '*paddingleft))
return message
def fullhelp():
longestleft = max(len(x[0]) for x in HELP_FULL)
width = shutil.get_terminal_size()[0] - 1
message = []
for item in HELP_FULL:
pad = width - (longestleft+ 3)
item = '%s : %s' % (item[0] + (' '*(longestleft - len(item[0]))), nicewrap(item[1], pad, longestleft + 3))
message.append(item)
message = '\n'.join(message)
return message
if __name__ == '__main__':
toddo = Toddo()
# Look, no more IndexErrors
sys.argv += [None]*10
if isinstance(sys.argv[1], str):
sys.argv[1] = sys.argv[1].lower()
if sys.argv[1] is None:
message = toddo.display_active_todos()
if message is None:
table = toddo.get_todotable()
print(HELP_NOACTIVE % table)
else:
print(message)
elif sys.argv[1] == 'all':
message = toddo.display_todos_from_table(None)
if message is None:
print(HELP_NOENTRIES)
else:
print(message)
elif sys.argv[1] == 'add':
args = list(filter(None, sys.argv))
args = args[2:]
args = ' '.join(args)
if args == '':
args = None
message = toddo.add_todo(args)
if isinstance(message, int):
print('Added %d' % message)
elif sys.argv[1] == 'remove':
idnumber = sys.argv[2]
if idnumber is None or not idnumber.replace(',', '').isdigit():
print(HELP_REMOVE)
else:
message = []
ids = [int(x) for x in idnumber.split(',')]
for x in ids:
try:
t = toddo.remove_todo(x)
message.append('Removed %d' % t)
except ToddoExc as e:
message.append(e.args[0])
print('\n'.join(message))
elif sys.argv[1] == 'table':
currenttable = toddo.get_todotable()
message = toddo.switch_todotable(sys.argv[2])
if currenttable == message:
print('You are on table `%s`' % message)
else:
print('Switched to table `%s`' % message)
elif sys.argv[1].isdigit():
try:
message = toddo.display_one_todo(int(sys.argv[1]))
print(message)
except ToddoExc as e:
print(e.args[0])
elif sys.argv[1] == 'help':
print(fullhelp())
else:
print('Command not recognized.')
print(fullhelp())