nejucomo avatar nejucomo committed f73f0f5

dagdo - Dependency and deadline aware task tracking.

Comments (0)

Files changed (1)

+#! /usr/bin/env python
+
+import os
+import sys
+import argparse
+import datetime
+import yaml
+
+
+DESCRIPTION = """
+Query a dagdo JSON file.
+"""
+
+
+def main(args = sys.argv[1:]):
+    opts = parse_args(args)
+    with file(opts.path) as f:
+        tasks = load_dag(f)
+
+    if has_failed():
+        raise SystemExit('Exiting due to errors.')
+    else:
+        for task in sorted(tasks):
+            print task
+
+
+def parse_args(args):
+    p = argparse.ArgumentParser(description=DESCRIPTION)
+    p.add_argument('--file', '-f',
+                   dest='path',
+                   default=os.path.expanduser('~/.dagdo.json'),
+                   help='The path to a dagdo file.')
+    return p.parse_args(args)
+
+
+def load_dag(f):
+    doc2dag = {} # Maps { id(docobj) -> Task }
+    tasks = set() # Set of depended-upon-leaf-nodes
+
+    for i, entry in enumerate(yaml.load(f)):
+        (title, due, docdeps, desc) = parse_entry_fields(i, entry)
+        nodedeps = set( doc2dag[id(d)] for d in docdeps )
+
+        node = Task(title, due, nodedeps, desc)
+        doc2dag[id(entry)] = node
+
+        if not node.has_dependencies():
+            tasks.add(node)
+
+    for task in tasks:
+        task.check_deadlines()
+
+    return tasks
+
+
+def parse_entry_fields(i, entry):
+    entry = entry.copy()
+    title = entry.pop('title')
+    due = entry.pop('due', None)
+    deps = entry.pop('deps', [])
+    desc = entry.pop('desc', None)
+    if entry:
+        fail('Unexpected keys in entry %d %r: %r', i, title, entry.keys())
+    return (title, due, deps, desc)
+
+
+def fail(tmpl, *args):
+    fail.failed = True
+    sys.stderr.write('Error: %s\n' % (tmpl % args,))
+
+fail.failed = False
+
+def has_failed():
+    return fail.failed
+
+
+class Task (object):
+    def __init__(self, title, due, deps, desc):
+        self.title = title
+        self.due = due
+        self.deps = deps
+        self.rdeps = set()
+        self.desc = desc
+
+        for dep in deps:
+            dep.rdeps.add(self)
+
+    def __repr__(self):
+        return '<%s %s>' % (self.__class__.__name__, self)
+
+    def __str__(self):
+        (due, rdep) = self.get_deadline_constraint()
+        because = '' if (rdep is self) else ' because of %s' % (rdep,)
+        return '%r due %s%s' % (self.title, fmt_datetime(due), because)
+
+    def __cmp__(self, other):
+        assert isinstance(other, Task), `other`
+        (mydue, _) = self.get_deadline_constraint()
+        (theirdue, _) = other.get_deadline_constraint()
+        return cmp(mydue, theirdue)
+
+    def has_dependencies(self):
+        return len(self.deps) > 0
+
+    def check_deadlines(self):
+        if self.rdeps:
+            for rdep in self.rdeps:
+                rdep.check_deadlines()
+        else:
+            if self.due is None:
+                fail('%r has no deadline.', self)
+
+    def get_deadline_constraint(self):
+        (due, because) = (self.due, self)
+        for rdep in self.rdeps:
+            (d, b) = rdep.get_deadline_constraint()
+            assert d is not None, 'invariant failure; get_deadline_constraint & check_deadlines do not agree.'
+            if due is None or d < due:
+                (due, because) = (d, b)
+        return (due, because)
+
+
+def fmt_datetime(dt):
+    assert isinstance(dt, datetime.date), `dt`
+    return '%04d-%02d-%02d' % (dt.year, dt.month, dt.day)
+
+
+
+if __name__ == '__main__':
+    main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.