# HG changeset patch # User Sascha L. Teichmann # Date 1218409967 -7200 # Node ID 2ccae1e872e91d7c4e19c90ef1e223df8a2067c9 # Parent feb6bb4427fefb084ad0ddf427148a0aefa81150 Initial checkin for project trees. !!!Work in progress!!! diff -r feb6bb4427fe -r 2ccae1e872e9 ChangeLog --- a/ChangeLog Mon Aug 04 12:32:46 2008 +0200 +++ b/ChangeLog Mon Aug 11 01:12:47 2008 +0200 @@ -1,3 +1,24 @@ +2008-08-11 Sascha L. Teichmann + + * getan: !!! Work in progress !!! Project Trees + The classic worklog only has one key to address a project. + This is major short comming. The getan database allows + to store projects keys with more than one character. + This feature will be exploited to build trees of projects. + + Lets say you have two main tasks foo and bar with two sub + tasks each you can name them f1, f2 and b1, b2. getan + stores these four tasks in a tree with top level f and b + and tow second level branches 1 and 2. To choose a task you + have to press f or b followed by 1 or 2. + + At the moment only selection works. + + TODO and next steps: + - Remove all old linear data structures. + - adjust rendering to only show active branches. + - sort anonymous task after naming into project tree. + 2008-08-04 Sascha L. Teichmann * contrib/zeiterfassung: Applied a variation of Stephan Holl's diff -r feb6bb4427fe -r 2ccae1e872e9 getan --- a/getan Mon Aug 04 12:32:46 2008 +0200 +++ b/getan Mon Aug 11 01:12:47 2008 +0200 @@ -277,15 +277,69 @@ }) cur.connection.commit() self.total += seconds - + +def build_tree(project, depth): + if len(project.key) == depth+1: + return ProjectNode(project, project.key[depth]) + node = ProjectNode(None, project.key[depth]) + node.children.append(build_tree(project, depth+1)) + return node + +class ProjectNode: + + def __init__(self, project = None, key = None): + self.children = [] + self.project = project + self.key = key + + def insertProject(self, project, depth = 0): + + if not project.key: # anonym -> end + node = ProjectNode(project) + self.children.append(node) + return + + for i, child in enumerate(self.children): + if not child.key: # before anonym projects + self.children.insert(i, build_tree(project, depth)) + return + if child.key == project.key[depth]: + child.insertProject(project, depth+1) + return + self.children.append(build_tree(project, depth)) + + def isLeaf(self): + return not self.project is None + + def findProject(self, key): + l, lower = key.lower(), None + for child in self.children: + if child.key == key: + return child + if child.key and child.key.lower() == l: + lower = child + return lower + + def dump(self, depth = 0): + out = [] + indent = " " * depth + out.append("%skey: %s" % (indent, self.key)) + if self.project: + out.append("%sdescription: %s" % (indent, self.project.desc)) + for child in self.children: + out.append(child.dump(depth+1)) + return "\n".join(out) class Worklog: def __init__(self, database): self.initDB(database) + self.projects = [] + self.tree = ProjectNode() + self.state = PAUSED + self.current_project = None + self.selection = self.tree self.loadProjects() - self.state = PAUSED - self.current_project = None def initDB(self, database): self.con = db.connect(database) @@ -295,7 +349,12 @@ try: cur = self.con.cursor() cur.execute(LOAD_ACTIVE_PROJECTS) - self.projects = [Project(*row) for row in cur.fetchall()] + while True: + row = cur.fetchone() + if not row: break + project = Project(*row) + self.projects.append(project) + self.tree.insertProject(project) finally: tolerantClose(cur) @@ -460,6 +519,7 @@ elif curses.ascii.isascii(c): if c == ord('-'): + self.selection = self.tree stdscr.erase() ofs = self.render() old_cur = cursor_visible(1) @@ -485,6 +545,7 @@ stdscr.refresh() elif c == ord('+'): + self.selection = self.tree stdscr.erase() ofs = self.render() old_cur = cursor_visible(1) @@ -512,16 +573,23 @@ stdscr.refresh() else: - nproject = self.findProject(chr(c)) - if nproject is None: return - self.current_project = nproject - nproject.start_time = datetime.now() - stdscr.erase() - ofs = self.render() - stdscr.refresh() - self.state = RUNNING - signal.signal(signal.SIGALRM, alarm_handler) - signal.alarm(1) + node = self.selection.findProject(chr(c)) + if not node: + self.selection = self.tree + return + if node.isLeaf(): + self.selection = self.tree + nproject = node.project + self.current_project = nproject + nproject.start_time = datetime.now() + stdscr.erase() + ofs = self.render() + stdscr.refresh() + self.state = RUNNING + signal.signal(signal.SIGALRM, alarm_handler) + signal.alarm(1) + else: + self.selection = node def runningState(self, c): global stdscr @@ -551,14 +619,23 @@ stdscr.refresh() elif curses.ascii.isascii(c): - nproject = self.findProject(chr(c)) - if nproject is None or nproject == self.current_project: + project_node = self.selection.findProject(chr(c)) + if project_node is None: + self.selection = self.tree return - nproject.start_time = self.writeLog() - self.current_project = nproject - stdscr.erase() - ofs = self.render() - stdscr.refresh() + + if project_node.isLeaf(): + self.selection = self.tree + nproject = project_node.project + if nproject == self.current_project: + return + nproject.start_time = self.writeLog() + self.current_project = nproject + stdscr.erase() + ofs = self.render() + stdscr.refresh() + else: + self.selection = project_node def pausedEscapeState(self, c): global stdscr