1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
#!/usr/bin/python3
import sys
import yaml
unknown = 0
blocked = 1
finished = 2
ready = 3
next = 4
goal = 5
statuses = {
'blocked': blocked,
'finished': finished,
'ready': ready,
'next': next,
'goal': goal,
}
def nodeattrs(done):
attrs = {
unknown: {'fillcolor': '#ff0000', 'shape': 'diamond'},
blocked: {'fillcolor': '#f4bada', 'shape': 'rectangle'},
finished: {'fillcolor': '#eeeeee'},
ready: {'fillcolor': '#ffffff'},
next: {'fillcolor': '#00cc00'},
goal: {'fillcolor': '#00eeee', 'shape': 'diamond'},
}
a = dict(attrs[done])
if 'style' not in a:
a['style'] = 'filled'
return ' '.join('{}="{}"'.format(key, a[key]) for key in a)
def find_unknown(tasklist):
return [t for t in tasklist if t['status'] == unknown]
def all_deps(tasks, task, status):
for dep_name in task.get('depends', []):
dep = tasks[dep_name]
if dep['status'] != status:
return False
return True
def any_dep(tasks, task, status):
for dep_name in task.get('depends', []):
dep = tasks[dep_name]
if dep['status'] == status:
return True
return False
def add_missing(tasks):
missing = {}
for task in tasks.values():
for dep in task.get('depends', []):
if dep not in tasks:
missing[dep] = {
'label': '{} IS MISSING'.format(dep),
}
tasks.update(missing)
def add_parents(tasks):
for task in tasks.values():
for dep in task.get('depends', []):
assert dep in tasks
dep = tasks[dep]
dep['parents'] = dep.get('parents', []) + [task]
def mark_goals(tasks):
for task in tasks.values():
if 'status' not in task:
if not task.get('parents'):
task['status'] = goal
def set_status(tasks):
add_missing(tasks)
add_parents(tasks)
mark_goals(tasks)
tasklist = list(tasks.values())
for task in tasklist:
if 'status' not in task:
task['status'] = unknown
else:
task['status'] = statuses[task['status']]
deps = [
dep
for dep in task.get('depends', [])
if dep in tasks
]
task['depends'] = deps
# for task in tasklist:
# print(task['status'], ' '.join(task['label'].split()))
# sys.exit(0)
unknown_tasks = find_unknown(tasklist)
while unknown_tasks:
for t in unknown_tasks:
if not t.get('depends', []):
t['status'] = ready
else:
if all_deps(tasks, t, finished):
t['status'] = ready
else:
t['status'] = blocked
unknown_tasks = find_unknown(tasklist)
obj = yaml.safe_load(sys.stdin)
ikiwiki = sys.argv[1:] == ['ikiwiki']
if ikiwiki:
print('[[!graph src="""')
else:
print('digraph "project" {')
tasks = obj.get('tasks', obj)
set_status(tasks)
for name, task in tasks.items():
print('{} [label="{}"]'.format(name, task['label']))
status = task['status']
print('{} [{}]'.format(name, nodeattrs(status)))
for dep in task.get('depends', []):
print('{} -> {}'.format(dep, name))
if ikiwiki:
print('"""]]')
else:
print('}')
|