summaryrefslogtreecommitdiff
path: root/scripts/depends.php
blob: 0621a4a53d6577c72e96d039877c8b88a2a426a8 (plain)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<?php

  /********************************************************\
  | Task Dependancy Graph                                  |
  | ~~~~~~~~~~~~~~~~~~~~~                                  |
  \********************************************************/

/**
 * XXX: This stuff looks incredible ugly, rewrite me for 1.0
 */

if (!defined('IN_FS')) {
    die('Do not access this file directly.');
}

if ( !($task_details = Flyspray::getTaskDetails(Req::num('task_id')))
        || !$user->can_view_task($task_details))
{
    Flyspray::show_error(9);
}

$id = Req::num('task_id');
$page->assign('task_id', $id);

$prunemode = Req::num('prune', 0);
$selfurl   = createURL('depends', $id);
$pmodes    = array(L('none'), L('pruneclosedlinks'), L('pruneclosedtasks'));

foreach ($pmodes as $mode => $desc) {
    if ($mode == $prunemode) {
        $strlist[] = $desc;
    } else {
        $strlist[] = "<a href='". htmlspecialchars($selfurl, ENT_QUOTES, 'utf-8') .
                      ($mode !=0 ? "&amp;prune=$mode" : "") . "'>$desc</a>\n";
    }
}

$page->uses('strlist');

$starttime = microtime();

$sql= 'SELECT t1.task_id AS id1, t1.item_summary AS sum1,
             t1.percent_complete AS pct1, t1.is_closed AS clsd1,
             lst1.status_name AS stat1, t1.task_severity AS sev1,
             t1.task_priority AS pri1,
             t1.closure_comment AS com1, u1c.real_name AS clsdby1,
             r1.resolution_name as res1,
             t2.task_id AS id2, t2.item_summary AS sum2,
             t2.percent_complete AS pct2, t2.is_closed AS clsd2,
             lst2.status_name AS stat2, t2.task_severity AS sev2,
             t2.task_priority AS pri2,
             t2.closure_comment AS com2, u2c.real_name AS clsdby2,
             r2.resolution_name as res2
       FROM  {dependencies} AS d
       JOIN  {tasks} AS t1 ON d.task_id=t1.task_id
  LEFT JOIN  {users} AS u1c ON t1.closed_by=u1c.user_id
  LEFT JOIN  {list_status} AS lst1 ON t1.item_status = lst1.status_id
  LEFT JOIN  {list_resolution} AS r1 ON t1.resolution_reason=r1.resolution_id
       JOIN  {tasks} AS t2 ON d.dep_task_id=t2.task_id
  LEFT JOIN  {list_status} AS lst2 ON t2.item_status = lst2.status_id
  LEFT JOIN  {users} AS u2c ON t2.closed_by=u2c.user_id
  LEFT JOIN  {list_resolution} AS r2 ON t2.resolution_reason=r2.resolution_id
      WHERE  t1.project_id= ?
   ORDER BY  d.task_id, d.dep_task_id';

$get_edges = $db->query($sql, array($proj->id));

$edge_list = array();
$rvrs_list = array();
$node_list = array();
while ($row = $db->fetchRow($get_edges)) {
    extract($row, EXTR_REFS);
    $edge_list[$id1][] = $id2;
    $rvrs_list[$id2][] = $id1;
    if (!isset($node_list[$id1])) {
        $node_list[$id1] =
	  array('id'=>$id1, 'sum'=>$sum1, 'pct'=>$pct1, 'clsd'=>$clsd1,
		'status_name'=>$stat1, 'sev'=>$sev1, 'pri'=>$pri1,
		'com'=>$com1, 'clsdby'=>$clsdby1, 'res'=>$res1);
    }
    if (!isset($node_list[$id2])) {
        $node_list[$id2] =
	  array('id'=>$id2, 'sum'=>$sum2, 'pct'=>$pct2, 'clsd'=>$clsd2,
		'status_name'=>$stat2, 'sev'=>$sev2, 'pri'=>$pri2,
		'com'=>$com2, 'clsdby'=>$clsdby2, 'res'=>$res2);
    }
}

// Now we have our lists of nodes and edges, along with a helper
// list of reverse edges. Time to do the graph coloring, so we know
// which ones are in our particular connected graph. We'll set up a
// list and fill it up as we visit nodes that are connected to our
// main task.

$connected  = array();
$levelsdown = 0;
$levelsup   = 0;
function connectsTo($id, $down, $up) {
    global $connected, $edge_list, $rvrs_list, $levelsdown, $levelsup;
    global $prunemode, $node_list;
    if (!isset($connected[$id])) { $connected[$id]=1; }
    if ($down > $levelsdown) { $levelsdown = $down; }
    if ($up   > $levelsup  ) { $levelsup   = $up  ; }

/*
echo '<pre><code>';
echo "$id ($down d, $up u) => $levelsdown d $levelsup u<br>\n";
echo 'nodes:';print_r($node_list);
echo 'edges:';print_r($edge_list);
echo 'rvrs:';print_r($rvrs_list);
echo 'levelsdown:';print_r($levelsdown);
echo "\n".'levelsup';print_r($levelsup);
echo '<code></pre>';
*/
    if (empty($node_list)){ return; }
    if (!isset($node_list[$id])){ return; }
    $selfclosed = $node_list[$id]['clsd'];
    if (isset($edge_list[$id])) {
        foreach ($edge_list[$id] as $neighbor) {
            $neighborclosed = $node_list[$neighbor]['clsd'];
            if (!isset($connected[$neighbor]) &&
                    !($prunemode==1 && $selfclosed && $neighborclosed) &&
                    !($prunemode==2 && $neighborclosed)) {
                connectsTo($neighbor, $down, $up+1);
            }
        }
    }
    if (isset($rvrs_list[$id])) {
        foreach ($rvrs_list[$id] as $neighbor) {
            $neighborclosed = $node_list[$neighbor]['clsd'];
            if (!isset($connected[$neighbor]) &&
                    !($prunemode==1 && $selfclosed && $neighborclosed) &&
                    !($prunemode==2 && $neighborclosed)) {
                connectsTo($neighbor, $down+1, $up);
            }
        }
    }
}

connectsTo($id, 0, 0);
$connected_nodes = array_keys($connected);
sort($connected_nodes);

// Now lets get rid of the extra junk in our arrays.
// In prunemode 0, we know we're only going to have to get rid of
// whole lists, and not elements in the lists, because if they were
// in the list, they'd be connected, so we wouldn't be removing them.
// In prunemode 1 or 2, we may have to remove stuff from the list, because
// you can have an edge to a node that didn't end up connected.
foreach (array("edge_list", "rvrs_list", "node_list") as $l) {
    foreach (${$l} as $n => $list) {
        if (!isset($connected[$n])) {
            unset(${$l}[$n]);
        }
        if ($prunemode!=0 && $l!="node_list" && isset(${$l}[$n])) {
            // Only keep entries that appear in the $connected_nodes list
            ${$l}[$n] = array_intersect(${$l}[$n], $connected_nodes);
        }
    }
}

// Now we've got everything we need... prepare JSON data
$resultData = array();
foreach ($node_list as $task_id => $taskInfo) {
	$adjacencies = array();
	if (isset($edge_list[$task_id])) {
		foreach ($edge_list[$task_id] as $dst) {
			array_push($adjacencies, array('nodeTo' => $dst, 'nodeFrom' => $task_id));
		}
	}

    if ($task_id == $id) {
        $color = '#5F9729';
    } else if ($taskInfo['clsd']) {
        $color = '#808080';
    } else {
        $color = '#83548B';
    }
    
	$newTask = array('id' => $task_id,
					 'name' => tpl_tasklink($task_id),
					 'data' => array('$color' => $color,
                                     '$type' => 'circle',
                                     '$dim' => 15),
                     'adjacencies' => $adjacencies);

	array_push($resultData, $newTask);
}

$jasonData = json_encode($resultData);
$page->assign('jasonData', $jasonData);
$page->assign('task_id', $id);

$page->setTitle(sprintf('FS#%d : %s', $id, L('dependencygraph')));
$page->pushTpl('depends.tpl');
?>