index : flyspray | |
Archlinux32 customized Flyspray installation | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | scripts/depends.php | 196 |
diff --git a/scripts/depends.php b/scripts/depends.php new file mode 100644 index 0000000..0621a4a --- /dev/null +++ b/scripts/depends.php @@ -0,0 +1,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 ? "&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'); +?> |