From aa1d4587cce0520bed21afb24a9fea9df2f8a623 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Mon, 30 Mar 2015 14:24:56 +0000 Subject: [PATCH] added a primitive LOC graph for a directory --- codepot/src/codepot/controllers/code.php | 2 + codepot/src/codepot/controllers/graph.php | 14 +- .../src/codepot/helpers/codepot_helper.php | 32 ++++ .../src/codepot/models/subversionmodel.php | 133 +++++++++++++- codepot/src/codepot/views/code_blame.php | 2 +- codepot/src/codepot/views/code_diff.php | 2 +- codepot/src/codepot/views/code_file.php | 6 +- codepot/src/codepot/views/code_folder.php | 165 ++++++++++++++++-- codepot/src/codepot/views/code_history.php | 2 +- codepot/src/codepot/views/code_revision.php | 2 +- codepot/src/codepot/views/file_home.php | 2 +- codepot/src/codepot/views/file_show.php | 2 +- codepot/src/codepot/views/graph_main.php | 17 +- codepot/src/codepot/views/issue_home.php | 2 +- codepot/src/codepot/views/project_home.php | 2 +- codepot/src/codepot/views/site_catalog.php | 3 + codepot/src/codepot/views/site_home.php | 2 +- codepot/src/css/common.css | 7 +- 18 files changed, 355 insertions(+), 42 deletions(-) diff --git a/codepot/src/codepot/controllers/code.php b/codepot/src/codepot/controllers/code.php index b7a7f7d3..2d746440 100644 --- a/codepot/src/codepot/controllers/code.php +++ b/codepot/src/codepot/controllers/code.php @@ -789,6 +789,8 @@ class Code extends Controller while (!feof($cloc)) { $line = @fgets ($cloc); + if ($line === FALSE) break; + if ($line_count == 2) { $counter = explode (':', $line); diff --git a/codepot/src/codepot/controllers/graph.php b/codepot/src/codepot/controllers/graph.php index 57e97c61..0b9f6478 100644 --- a/codepot/src/codepot/controllers/graph.php +++ b/codepot/src/codepot/controllers/graph.php @@ -70,7 +70,7 @@ class Graph extends Controller } } - function history_json ($projectid = '', $path = '', $rev = SVN_REVISION_HEAD) + function history_json ($projectid = '', $path = '') { $this->load->model ('ProjectModel', 'projects'); @@ -113,7 +113,7 @@ class Graph extends Controller print codepot_json_encode ($history); } - function total_loc_json ($projectid) + function folder_loc_json ($projectid = '', $path = '', $rev = SVN_REVISION_HEAD) { $this->load->model ('ProjectModel', 'projects'); @@ -131,7 +131,13 @@ class Graph extends Controller return; } - // check out the whole project - // run clock.pl againt it. + $this->load->model ('SubversionModel', 'subversion'); + + $path = $this->converter->HexToAscii ($path); + if ($path == '.') $path = ''; /* treat a period specially */ + $path = $this->_normalize_path ($path); + + $cloc = $this->subversion->clocRev ($projectid, $path, $rev); + print codepot_json_encode ($cloc); } } diff --git a/codepot/src/codepot/helpers/codepot_helper.php b/codepot/src/codepot/helpers/codepot_helper.php index 6dc1b357..0d1341c3 100644 --- a/codepot/src/codepot/helpers/codepot_helper.php +++ b/codepot/src/codepot/helpers/codepot_helper.php @@ -110,3 +110,35 @@ if ( !function_exists ('codepot_json_encode')) } +if ( !function_exists ('codepot_delete_files')) +{ + function codepot_delete_files($path, $del_dir = FALSE, $level = 0) + { + // Trim the trailing slash + $path = rtrim($path, DIRECTORY_SEPARATOR); + + if ( ! $current_dir = @opendir($path)) + return; + + while(FALSE !== ($filename = @readdir($current_dir))) + { + if ($filename != "." && $filename != "..") + { + if (is_dir($path.DIRECTORY_SEPARATOR.$filename)) + { + codepot_delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $level + 1); + } + else + { + @unlink($path.DIRECTORY_SEPARATOR.$filename); + } + } + } + @closedir($current_dir); + + if ($del_dir == TRUE /*&& $level > 0*/) + { + @rmdir($path); + } + } +} diff --git a/codepot/src/codepot/models/subversionmodel.php b/codepot/src/codepot/models/subversionmodel.php index f787fa1d..024804a9 100644 --- a/codepot/src/codepot/models/subversionmodel.php +++ b/codepot/src/codepot/models/subversionmodel.php @@ -101,8 +101,7 @@ class SubversionModel extends Model $fileinfo['properties'] = $prop[$orgurl]; else $fileinfo['properties'] = NULL; - $fileinfo['fullpath'] = substr ( - $info[0]['url'], strlen($info[0]['repos'])); + $fileinfo['fullpath'] = substr ($info[0]['url'], strlen($info[0]['repos'])); $fileinfo['name'] = $info[0]['path']; $fileinfo['type'] = 'dir'; $fileinfo['size'] = 0; @@ -234,7 +233,7 @@ class SubversionModel extends Model $url = 'file://'.$this->_canonical_path(CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"); /* Compose a URL with a peg revision if a specific revision is given. However, - * It skipps composition if the path indicates the project root. Read the comment + * It skips composition if the path indicates the project root. Read the comment * in getFile() to know more about this skipping. */ if ($rev != SVN_REVISION_HEAD && $path != '') $url = $url . '@' . $rev; @@ -839,6 +838,134 @@ class SubversionModel extends Model @svn_auth_set_parameter (SVN_AUTH_PARAM_DEFAULT_USERNAME, $orguser); return $result; } + + + function _cloc_revision ($projectid, $path, $rev) + { + $orgurl = 'file://'.$this->_canonical_path(CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"); + + $workurl = ($path == '')? $orgurl: "{$orgurl}@"; // trailing @ for collision prevention + $info = @svn_info ($workurl, FALSE, $rev); + if ($info === FALSE || count($info) != 1) + { + // If a URL is at the root of repository and the url type is file:// + // (e.g. file:///svnrepo/codepot/ where /svnrepo/codepot is a project root), + // some versions of libsvn end up with an assertion failure like + // ... libvvn_subr/path.c:114: svn_path_join: Assertion `svn_path_is_canonical(base, pool)' failed. + // + // Since the root directory is guaranteed to exist at the head revision, + // the information can be acquired without using a peg revision. + // In this case, a normal operational revision is used to work around + // the assertion failure. Other functions that has to deal with + // the root directory should implement this check to work around it. + // + if ($rev == SVN_REVISION_HEAD || $path == '') return FALSE; + + // rebuild the URL with a peg revision and retry it. + $workurl = "{$orgurl}@{$rev}"; + $info = @svn_info ($workurl, FALSE, $rev); + if ($info === FALSE || count($info) != 1) return FALSE; + } + + if ($info[0]['kind'] == SVN_NODE_FILE) return FALSE; + + // pass __FILE__ as the first argument so that tempnam creates a name + // in the system directory. __FILE__ can never be a valid directory. + $tfname = @tempnam(__FILE__, 'codepot-cloc-rev-'); + if ($tfname === FALSE) return FALSE; + + $actual_tfname = $tfname . '.d'; + codepot_delete_files ($actual_tfname, TRUE); // delete the directory in case it exists + + if (svn_checkout ($workurl, $actual_tfname, $rev, 0) === FALSE) + { + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + + $cloc_cmd = sprintf ('%s --quiet --csv --csv-delimiter=":" %s', CODEPOT_CLOC_COMMAND_PATH, $actual_tfname); + $cloc = @popen ($cloc_cmd, 'r'); + if ($cloc === FALSE) + { + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + + $line_count = 0; + $cloc_data = array (); + while (!feof($cloc)) + { + $line = @fgets ($cloc); + if ($line === FALSE) break; + + $line_count++; + $line = trim($line); + if ($line_count >= 3) + { + $counter = explode (':', $line); + $cloc_data[$counter[1]] = array ($counter[0], $counter[2], $counter[3], $counter[4]); + } + } + + @pclose ($cloc); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + + return $cloc_data; + } + + function clocRev ($projectid, $path, $rev) + { + return $this->_cloc_revision ($projectid, $path, $rev); + } + + function clocRevBySubdir ($projectid, $path, $rev) + { + $orgurl = 'file://'.$this->_canonical_path(CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"); + + $workurl = ($path == '')? $orgurl: "{$orgurl}@"; // trailing @ for collision prevention + $info = @svn_info ($workurl, FALSE, $rev); + if ($info === FALSE || count($info) != 1) + { + // If a URL is at the root of repository and the url type is file:// + // (e.g. file:///svnrepo/codepot/ where /svnrepo/codepot is a project root), + // some versions of libsvn end up with an assertion failure like + // ... libvvn_subr/path.c:114: svn_path_join: Assertion `svn_path_is_canonical(base, pool)' failed. + // + // Since the root directory is guaranteed to exist at the head revision, + // the information can be acquired without using a peg revision. + // In this case, a normal operational revision is used to work around + // the assertion failure. Other functions that has to deal with + // the root directory should implement this check to work around it. + // + if ($rev == SVN_REVISION_HEAD || $path == '') return FALSE; + + // rebuild the URL with a peg revision and retry it. + $workurl = "{$orgurl}@{$rev}"; + $info = @svn_info ($workurl, FALSE, $rev); + if ($info === FALSE || count($info) != 1) return FALSE; + } + + if ($info[0]['kind'] == SVN_NODE_FILE) return FALSE; + + $lsinfo = @svn_ls ($workurl, $rev, FALSE, TRUE); + + $cloc_data = array (); + foreach ($lsinfo as $key => $value) + { + if ($value['type'] == 'dir') + { + $cloc = $this->_cloc_revision ($projectid, "{$path}/{$key}", $rev); + if ($cloc === FALSE) return FALSE; + + $cloc_data[$key] = $cloc; + } + } + + return $cloc_data; + } } ?> diff --git a/codepot/src/codepot/views/code_blame.php b/codepot/src/codepot/views/code_blame.php index 00669a46..c2dab8e2 100644 --- a/codepot/src/codepot/views/code_blame.php +++ b/codepot/src/codepot/views/code_blame.php @@ -172,7 +172,7 @@ print anchor ("code/fetch/{$project->id}/${xpar}{$revreq}", $this->lang->line('D lang->line('Details')?> -
+
load->view ( if ($fileext == "") $fileext = "html" ?> -
+ -
+
0)
LOC
id}/{$xpar}{$revreq}"); - print ""; + $graph_url = codepot_merge_path (site_url(), "/code/graph/cloc-file/{$project->id}/{$xpar}{$revreq}"); + print ""; ?>
diff --git a/codepot/src/codepot/views/code_folder.php b/codepot/src/codepot/views/code_folder.php index 2f545e2f..7d9f449c 100644 --- a/codepot/src/codepot/views/code_folder.php +++ b/codepot/src/codepot/views/code_folder.php @@ -12,11 +12,127 @@ + + + + + + + + + converter->AsciiToHex('.') . $revreq; + } ?> @@ -84,17 +221,6 @@ $this->load->view (
converter->AsciiToHex('.') . $revreq; - } - // print the main anchor for the root folder. // let the anchor text be the project name. print anchor ( @@ -144,10 +270,14 @@ $this->load->view (
-
+
+ +
+
+
load->view ( { // file $hexpath = $this->converter->AsciiToHex($fullpath); - print ""; + print ""; print ''; print anchor ( "code/file/{$project->id}/{$hexpath}{$revreq}", @@ -308,7 +438,14 @@ $this->load->view ( print ''; } + + print '
LOC
'; + print ''; + print $this->lang->line('Graph'); + print ''; + print '
'; + } ?> diff --git a/codepot/src/codepot/views/code_history.php b/codepot/src/codepot/views/code_history.php index 489bc96b..bf0a7f77 100644 --- a/codepot/src/codepot/views/code_history.php +++ b/codepot/src/codepot/views/code_history.php @@ -78,7 +78,7 @@ $this->load->view ( -
+
converter->AsciiToHex (($fullpath == '')? '.': $fullpath); diff --git a/codepot/src/codepot/views/code_revision.php b/codepot/src/codepot/views/code_revision.php index 07b3c227..e5b8bfde 100644 --- a/codepot/src/codepot/views/code_revision.php +++ b/codepot/src/codepot/views/code_revision.php @@ -284,7 +284,7 @@ $history = $file['history'];
-
+
lang->line('Message')?>  diff --git a/codepot/src/codepot/views/file_home.php b/codepot/src/codepot/views/file_home.php index ce2dddf8..f2b2e563 100644 --- a/codepot/src/codepot/views/file_home.php +++ b/codepot/src/codepot/views/file_home.php @@ -45,7 +45,7 @@ $this->load->view (
lang->line('Files')?>
-
+
load->view (
-
+
diff --git a/codepot/src/codepot/views/graph_main.php b/codepot/src/codepot/views/graph_main.php index f0e2c2b0..df400f53 100644 --- a/codepot/src/codepot/views/graph_main.php +++ b/codepot/src/codepot/views/graph_main.php @@ -21,7 +21,6 @@ - @@ -229,7 +228,7 @@ function show_commits_per_month_graph(log) //show_tooltip(item.pageX, item.pageY, '(' + item.datapoint[0] + ', ' + item.datapoint[1]+')'); show_tooltip ("graph_main_commits_per_month_tooltip", item.pageX, item.pageY - 20, - '(' + datestr + ',' + item.datapoint[1] + ')'); + '(' + datestr + ',' + item.datapoint[1] + ')'); } } else @@ -393,11 +392,17 @@ function show_all_graphs (response) if (log == null) { alert ('Invalid data received'); - return; } - - show_commits_per_month_graph (log); - show_commits_per_user_graph (log); + else if (log.length > 0) + { + show_commits_per_month_graph (log); + show_commits_per_user_graph (log); + } + else + { + //$("#graph_main_commits_per_month").text("No data available"); + alert ('No data available'); + } } function render_graphs() diff --git a/codepot/src/codepot/views/issue_home.php b/codepot/src/codepot/views/issue_home.php index 0f2b6c98..c890d53c 100644 --- a/codepot/src/codepot/views/issue_home.php +++ b/codepot/src/codepot/views/issue_home.php @@ -154,7 +154,7 @@ $this->load->view (
-
+
name)?>
-
+
diff --git a/codepot/src/codepot/views/site_catalog.php b/codepot/src/codepot/views/site_catalog.php index 38c719f5..3ebeb779 100644 --- a/codepot/src/codepot/views/site_catalog.php +++ b/codepot/src/codepot/views/site_catalog.php @@ -46,6 +46,8 @@ $this->load->view (
+ +
+
diff --git a/codepot/src/codepot/views/site_home.php b/codepot/src/codepot/views/site_home.php index 27b5b676..2df896d3 100644 --- a/codepot/src/codepot/views/site_home.php +++ b/codepot/src/codepot/views/site_home.php @@ -286,7 +286,7 @@ foreach ($latest_projects as $project) -
+
diff --git a/codepot/src/css/common.css b/codepot/src/css/common.css index 61a925e9..8efc07ee 100644 --- a/codepot/src/css/common.css +++ b/codepot/src/css/common.css @@ -88,9 +88,6 @@ body { .content .taskbar a:active { } - - - .content .taskbar form { margin: 0; } @@ -116,6 +113,10 @@ body { vertical-align: middle; } +.content .mainarea .result { + min-height: 30em; +} + .content .mainarea form input:focus { background-color: #E7E8E7; outline: 0;