switching to new GUI, remove CmdWindow

This commit is contained in:
Josh Holtrop 2011-02-08 22:07:08 -05:00
parent 9af4c655b1
commit 5fc2cb8a53
6 changed files with 23 additions and 668 deletions

View File

@ -1,103 +0,0 @@
import gtk
import gobject
from datetime import datetime
class CmdWindow:
def __init__(self, handle_activated):
self.starttime = None
self.handle_activated = handle_activated
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_geometry_hints(min_width = 350);
self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", self.destroy_event)
self.entry = gtk.Entry()
self.entry.connect("activate", self.activate_event)
self.button = gtk.Button(stock = gtk.STOCK_OK)
self.button.connect("clicked", self.activate_event)
self.status_hbox = gtk.HBox()
self.status_label = gtk.Label('')
self.elapsed_label = gtk.Label('')
self.status_hbox.pack_start(self.status_label, expand = False)
self.status_hbox.pack_end(self.elapsed_label, expand = False)
self.error_label = gtk.Label('')
self.formatted_label = gtk.Label('')
vbox = gtk.VBox()
vbox.pack_start(self.status_hbox)
vbox.pack_start(self.error_label)
vbox.pack_start(self.formatted_label)
gobject.timeout_add(1000, self.updateElapsed)
hbox = gtk.HBox()
hbox.pack_start(self.entry)
hbox.pack_start(self.button, expand = False)
hbox.show_all()
vbox.pack_start(hbox)
vbox.show()
self.window.add(vbox)
def main(self):
self.window.show()
gtk.main()
def delete_event(self, widget, event, data=None):
return False
def destroy_event(self, widget, data=None):
gtk.main_quit()
def activate_event(self, widget, data=None):
if not self.handle_activated(self.entry.get_text(), self):
self.window.destroy()
self.entry.set_text('')
def getElapsed(self):
if self.starttime is None:
return ''
delta = datetime.now() - self.starttime
days = delta.days
dsecs = delta.seconds
hours = dsecs / (60 * 60)
dsecs -= hours * 60 * 60
minutes = dsecs / 60
dsecs -= minutes * 60
seconds = dsecs
elapsed = ''
if days != 0:
elapsed = '%dd ' % days
elapsed += '%d:%02d:%02d' % (hours, minutes, seconds)
return elapsed
def updateElapsed(self):
self.elapsed_label.set_text(self.getElapsed())
return True
def setStatus(self, status, starttime):
if status != '':
self.status_label.set_text(status)
self.starttime = starttime
self.updateElapsed()
self.status_hbox.show_all()
else:
self.status_hbox.hide_all()
def setError(self, error):
self.error_label.set_text(error)
if error != '':
self.error_label.show()
else:
self.error_label.hide()
def setFormatted(self, formatted):
self.formatted_label.set_text(formatted)
if formatted != '':
self.formatted_label.show()
else:
self.formatted_label.hide()

View File

@ -1,116 +0,0 @@
from datetime import datetime, timedelta
import re
class Command:
def __init__(self, cmdline, default_time = None):
if default_time is None:
self.time = datetime.now()
else:
self.time = default_time
self.command = 'start'
self.argstr = ''
self.parseCommandLine(cmdline)
def __str__(self):
return "{'time' => '%s', 'command' => '%s', 'argstr' => '%s'}" % \
(self.time, self.command, self.argstr)
def parseCommandLine(self, cmdline):
COMMANDS = {
'out' : 1,
'report' : 1,
'status' : 1,
'fill' : 1,
'adjust' : 1,
'start' : 1,
'task' : 1
}
ALIASES = {
'rpt' : 'report',
'adj' : 'adjust',
'end' : 'out',
'st' : 'status',
'show' : 'status',
'f' : 'fill'
}
args = []
foundArgs = False
while True:
cmdline = cmdline.strip()
if len(cmdline) < 1:
break
parts = cmdline.split(None, 1)
token = parts[0]
if not foundArgs:
if self.parseDate(token):
pass
elif self.parseTime(token):
pass
elif token in COMMANDS:
self.command = token
foundArgs = True
elif token in ALIASES:
self.command = ALIASES[token]
foundArgs = True
else:
foundArgs = True
args.append(token)
else:
args.append(token)
if len(parts) < 2:
break
cmdline = parts[1]
self.argstr = ' '.join(args)
def parseDate(self, dt):
if dt.lower() == "yesterday":
today = datetime.today()
y = today - timedelta(days = 1)
self.time = self.time.replace(
year = y.year, month = y.month, day = y.day)
return True
m = re.match('^(?:(\d{4})[-/])?(\d{1,2})[-/](\d{1,2})$', dt)
if m is not None:
# dt was a date string
if m.group(1) is not None:
self.time = self.time.replace(year = int(m.group(1)))
month, day = int(m.group(2)), int(m.group(3))
self.time = self.time.replace(month = month, day = day)
return True
return False
def parseTime(self, timespec):
m = re.match('^(\d{1,2}):?(\d{2})?(am?|pm?)?$', timespec, re.I)
if m is not None:
# an absolute time was given
h = int(m.group(1))
mins = 0 if m.group(2) is None else int(m.group(2))
am_pm = '' if m.group(3) is None else m.group(3)[0].lower()
if am_pm == 'p' and h < 12:
h += 12
elif am_pm == 'a' and h == 12:
h = 0
self.time = self.time.replace(hour = h, minute = mins, second = 0)
return True
m = re.match('^([-+])(\d+(?:\.\d+)?)([hms])?$', timespec, re.I)
if m is not None:
# a relative time was given
plus_minus = m.group(1)
hms = '' if m.group(3) is None else m.group(3)
count = int(m.group(2))
if hms == 'm':
seconds = count * 60
elif hms == 's':
seconds = count
else: # hours
seconds = count * 60 * 60
delta = timedelta(seconds = seconds)
if plus_minus == '-':
self.time -= delta
else:
self.time += delta
return True
return False

View File

@ -1,267 +0,0 @@
import sqlite3
import os
from datetime import datetime
HISTORY_DT_FMT = '%Y-%m-%d %H:%M:%S'
class Task:
def __init__(self, taskid, name, longname, parentid):
self.taskid = taskid
self.name = name
self.longname = longname
self.parentid = parentid
class TaskRef:
def __init__(self, taskid, time):
self.taskid = taskid
self.time = time
class Entry:
def __init__(self, date, seconds, taskid):
self.date = date
self.seconds = seconds
self.taskid = taskid
class DataStore:
def __init__(self, dbfile):
if not os.path.exists(dbfile):
self.createdb(dbfile)
self.conn = sqlite3.connect(dbfile)
def __del__(self):
self.conn.close()
def createdb(self, dbfile):
conn = sqlite3.connect(dbfile)
c = conn.cursor()
c.execute('''
CREATE TABLE tasks (
id INTEGER PRIMARY KEY,
name TEXT,
longname TEXT,
parentid INTEGER,
FOREIGN KEY (parentid) REFERENCES tasks(id)
)''')
c.execute('''
CREATE TABLE entries (
date TEXT,
taskid INTEGER,
seconds INTEGER,
PRIMARY KEY (date, taskid),
FOREIGN KEY (taskid) REFERENCES tasks(id)
)''')
c.execute('''
CREATE TABLE history (
id INTEGER PRIMARY KEY,
taskid INTEGER,
datetime TEXT,
FOREIGN KEY (taskid) REFERENCES tasks(id)
)''')
conn.commit()
c.close()
conn.close()
def getCurrentTask(self):
c = self.conn.cursor()
c.execute('''
SELECT taskid, datetime
FROM history
WHERE id = 0
''')
ct = None
for row in c:
taskid = row[0]
dt = datetime.strptime(row[1], HISTORY_DT_FMT)
ct = TaskRef(taskid, dt)
c.close()
self.conn.commit()
return ct
def updateCurrentTask(self, ct):
self.clearCurrentTask()
c = self.conn.cursor()
c.execute('''
INSERT INTO history
VALUES (0, ?, ?)
''', (ct.taskid, ct.time.strftime(HISTORY_DT_FMT)))
c.close()
self.conn.commit()
def clearCurrentTask(self):
c = self.conn.cursor()
c.execute('''
DELETE FROM history
WHERE id = 0
''')
c.close()
self.conn.commit()
def updateTask(self, taskid, name, longname):
c = self.conn.cursor()
c.execute('''
UPDATE tasks
SET name = ?, longname = ?
WHERE id = ?
''', (name, longname, taskid))
c.close()
self.conn.commit()
return True
def getTaskByID(self, taskid):
t = None
c = self.conn.cursor()
c.execute('''
SELECT name, longname, parentid
FROM tasks
WHERE id = ?
''', (taskid,))
for row in c:
t = Task(taskid, row[0], row[1], row[2])
c.close()
self.conn.commit()
return t
def getTaskByNameParent(self, name, parentid):
t = None
c = self.conn.cursor()
name = name.strip()
if parentid is None:
c.execute('''
SELECT id, name, longname, parentid
FROM tasks
WHERE name = ? AND parentid IS NULL
''', (name,))
else:
c.execute('''
SELECT id, name, longname, parentid
FROM tasks
WHERE name = ? AND parentid = ?
''', (name, parentid))
for row in c:
t = Task(*row)
c.close()
self.conn.commit()
return t
def getTaskByName(self, name):
parts = name.split(':')
parentid = None
task = None
for p in parts:
p = p.strip()
task = self.getTaskByNameParent(p, parentid)
if task is None:
break
parentid = task.taskid
return task
def getTaskByShortName(self, name):
if name.find(':') >= 0:
return self.getTaskByName(name)
task = None
c = self.conn.cursor()
c.execute('''
SELECT id, name, longname, parentid
FROM tasks
WHERE name = ?
''', (name,))
count = 0
for row in c:
count += 1
task = Task(*row)
if count > 1:
return None
c.close()
self.conn.commit()
return task
def getParentTaskByName(self, name):
parts = name.split(':')
parentname = ':'.join(parts[:-1])
return self.getTaskByName(parentname)
def getParentTaskByShortName(self, name):
parts = name.split(':')
parentname = ':'.join(parts[:-1])
return self.getTaskByShortName(parentname)
def getTaskPath(self, task):
path = task.name
if task.parentid is not None:
parenttask = self.getTaskByID(task.parentid)
if parenttask is not None:
parentpath = self.getTaskPath(parenttask)
path = parentpath + ':' + path
return path
def createTask(self, name, longname, parentid):
c = self.conn.cursor()
if parentid is not None and parentid != '':
c.execute('''
SELECT *
FROM tasks
WHERE id = ?
''', (parentid,))
found = False
for row in c:
found = True
if not found:
return 0
c.execute('''
SELECT MAX(id)
FROM tasks
''')
nextid = 1
for row in c:
if row[0] is not None:
nextid = row[0] + 1
c.execute('''
INSERT INTO tasks
VALUES (?, ?, ?, ?)
''', (nextid, name.strip(), longname.strip(), parentid))
c.close()
self.conn.commit()
return nextid
def addTime(self, date, taskid, seconds):
c = self.conn.cursor()
exists = False
oldseconds = 0
c.execute('''
SELECT seconds
FROM entries
WHERE date = ? AND taskid = ?
''', (date, taskid))
for row in c:
if row[0] is not None:
exists = True
oldseconds = row[0]
if exists:
c.execute('''
UPDATE entries
SET seconds = ?
WHERE date = ? AND taskid = ?
''', (oldseconds + seconds, date, taskid))
else:
c.execute('''
INSERT INTO entries
VALUES(?, ?, ?)
''', (date, taskid, seconds))
c.close()
self.conn.commit()
def getEntriesInDateRange(self, date1, date2):
entries = []
c = self.conn.cursor()
c.execute('''
SELECT date, seconds, taskid
FROM entries
WHERE date >= ? AND date <= ?
ORDER BY taskid, date
''', (date1.strftime('%Y-%m-%d'), date2.strftime('%Y-%m-%d')))
for row in c:
entries.append(Entry(*row))
c.close()
self.conn.commit()
return entries

20
Window.py Normal file
View File

@ -0,0 +1,20 @@
import gtk
import gobject
class Window:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_geometry_hints();
self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", self.destroy_event)
def main(self):
self.window.show()
gtk.main()
def delete_event(self, widget, event, data=None):
return False
def destroy_event(self, widget, data=None):
gtk.main_quit()

136
dwtt
View File

@ -3,11 +3,8 @@
import os import os
import sys import sys
import getopt import getopt
from datetime import datetime, timedelta
from CmdWindow import CmdWindow from Window import *
from Command import Command
from DataStore import DataStore, TaskRef
PROGRAM_NAME = 'dwtt' PROGRAM_NAME = 'dwtt'
@ -32,135 +29,8 @@ def main(argv):
usage() usage()
sys.exit(3) sys.exit(3)
timedbfile = os.path.expanduser('~') + os.path.sep + '.dwtt.db' w = Window()
if len(args) >= 1: w.main()
timedbfile = args[0]
ds = DataStore(timedbfile)
def handleActivated(cmdline, cw):
if cmdline.strip() == '':
return False
ct = ds.getCurrentTask()
if ct is not None:
task = ds.getTaskByID(ct.taskid)
status = 'Task: ' + ds.getTaskPath(task)
if task.longname != '':
status += ' (%s)' % task.longname
cw.setStatus(status, ct.time)
else:
cw.setStatus('', None)
cmd = Command(cmdline)
res = processCommand(cmd, ds)
if not res.keepwindow:
return False
cw.setError(res.error)
cw.setFormatted(res.formatted)
return True
cw = CmdWindow(handleActivated)
ct = ds.getCurrentTask()
if ct is not None:
task = ds.getTaskByID(ct.taskid)
status = 'Task: ' + ds.getTaskPath(task)
if task.longname != '':
status += ' (%s)' % task.longname
cw.setStatus(status, ct.time)
cw.main()
class Result:
def __init__(self):
self.keepwindow = False
self.error = ''
self.message = ''
self.formatted = ''
def processStart(cmd, store):
res = Result()
task = store.getTaskByShortName(cmd.argstr)
if task is None:
parent = store.getParentTaskByShortName(cmd.argstr)
if parent is None:
res.error = 'Could not find task "%s"' % \
(':'.join(cmd.argstr.split(':')[:-1]))
res.keepwindow = True
return res
taskid = store.createTask(cmd.argstr.split(':')[-1], '', parent.taskid)
else:
taskid = task.taskid
processOut(cmd, store)
store.updateCurrentTask(TaskRef(taskid, cmd.time))
return res
def processOut(cmd, store):
res = Result()
ct = store.getCurrentTask()
if ct is None:
res.error = 'No current task defined'
res.keepwindow = True
return res
seconds = (cmd.time - ct.time).seconds
if seconds > 0:
store.addTime(cmd.time.strftime('%Y-%m-%d'), ct.taskid, seconds)
store.clearCurrentTask()
return res
def processTask(cmd, store):
res = Result()
res.keepwindow = True
parts = cmd.argstr.split(',', 1)
fullname = parts[0].strip()
longname = '' if len(parts) < 2 else parts[1].strip()
nameparts = fullname.split(':')
task = store.getTaskByName(fullname)
if task is not None:
# the task already exists, update it
store.updateTask(task.taskid, nameparts[-1], longname)
res.message = 'Task "%s" updated' % fullname
return res
if len(nameparts) > 1:
parenttask = store.getParentTaskByShortName(fullname)
if parenttask is None:
res.error = 'Parent task of "%s" not found' % fullname
return res
store.createTask(nameparts[-1].strip(), longname, parenttask.taskid)
else:
store.createTask(nameparts[-1].strip(), longname, None)
res.message = 'Task "%s" created' % fullname
return res
def processStatus(cmd, store):
res = Result()
res.keepwindow = True
now = datetime.now()
monday = now - timedelta(now.weekday())
sunday = monday + timedelta(6)
entries = store.getEntriesInDateRange(monday, sunday)
prevtask = 0
for ent in entries:
if ent.taskid != prevtask:
prevtask = ent.taskid
task = store.getTaskByID(ent.taskid)
print store.getTaskPath(task) + ':'
hours = round(float(ent.seconds) / (60 * 60), 1)
print ' %s: %0.1f' % (ent.date, hours)
return res
COMMAND_HANDLERS = {
'start' : processStart,
'out' : processOut,
'task' : processTask,
'status' : processStatus
}
# Returns boolean for whether the command prompt should be displayed again
def processCommand(cmd, store):
if cmd.command in COMMAND_HANDLERS:
return COMMAND_HANDLERS[cmd.command](cmd, store)
res = Result()
res.error = 'Unknown command: %s' % cmd.command
res.keepwindow = True
return res
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv) main(sys.argv)

49
test
View File

@ -1,49 +0,0 @@
#!/usr/bin/env python
from Command import Command
from datetime import datetime
n_tests = 0
n_pass = 0
def testcmd(cmdline, cmd, argstr):
global n_tests, n_pass
n_tests += 1
print "Testing command line '%s'" % cmdline
c = Command(cmdline)
if c.command == cmd and c.argstr == argstr:
n_pass += 1
else:
print " **** testcmd FAILED ****"
print " command:"
print " expected: '%s'" % cmd
print " actual: '%s'" % c.command
print " argstr:"
print " expected: '%s'" % argstr
print " actual: '%s'" % c.argstr
print " ************************"
def main():
global n_tests, n_pass
testcmd('out', 'out', '')
testcmd('fill', 'fill', '')
testcmd('adjust', 'adjust', '')
testcmd('2 report', 'report', '')
testcmd('show', 'status', '')
testcmd('5pm out', 'out', '')
testcmd('st 45h', 'status', '45h')
testcmd('fill 40h', 'fill', '40h')
testcmd('-10m arlx', 'start', 'arlx')
testcmd(' adjust -10m ', 'adjust', '-10m')
testcmd(' 9:45 wr: nog: pbit ram test ',
'start', 'wr: nog: pbit ram test')
testcmd('12/14 3pm start arlx', 'start', 'arlx')
testcmd('yesterday 7P out', 'out', '')
if n_tests == n_pass:
print " >= SUCCESS <="
else:
print " >= FAIL <="
print "%d of %d tests pass" % (n_pass, n_tests)
if __name__ == "__main__":
main();