2023-08-23 14:24:52 +02:00
#!/usr/bin/env php
< ? php
/*
* Copyright ( C ) 2023 Laurent Destailleur < eldy @ users . sourceforge . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*/
/**
* \file dev / tools / apstats . php
2024-01-13 11:40:04 +01:00
* \brief Script to report Advanced Statistics on a coding PHP project
2023-08-23 14:24:52 +02:00
*/
$sapi_type = php_sapi_name ();
$script_file = basename ( __FILE__ );
$path = dirname ( __FILE__ ) . '/' ;
// Test si mode batch
$sapi_type = php_sapi_name ();
if ( substr ( $sapi_type , 0 , 3 ) == 'cgi' ) {
echo " Error: You are using PHP for CGI. To execute " . $script_file . " from command line, you must use PHP for CLI mode. \n " ;
exit ();
}
2023-12-04 10:22:29 +01:00
error_reporting ( E_ALL & ~ E_DEPRECATED );
2023-08-23 14:24:52 +02:00
define ( 'PRODUCT' , " apstats " );
define ( 'VERSION' , " 1.0 " );
2024-01-15 02:54:44 +01:00
$phpstanlevel = 3 ;
2023-12-15 10:28:16 +01:00
2024-02-11 13:35:49 +01:00
// Include Dolibarr environment
require_once $path . '../../htdocs/master.inc.php' ;
require_once $path . '../../htdocs/core/lib/files.lib.php' ;
require_once $path . '../../htdocs/core/lib/geturl.lib.php' ;
2023-08-23 14:24:52 +02:00
print '***** ' . constant ( 'PRODUCT' ) . ' - ' . constant ( 'VERSION' ) . ' *****' . " \n " ;
if ( empty ( $argv [ 1 ])) {
print 'You must run this tool being into the root of the project.' . " \n " ;
2024-02-11 13:35:49 +01:00
print 'Usage: ' . constant ( 'PRODUCT' ) . '.php pathto/outputfile.html [--dir-scc=pathtoscc|disabled] [--dir-phpstan=pathtophpstan|disabled]' . " \n " ;
2023-09-22 04:36:44 +02:00
print 'Example: ' . constant ( 'PRODUCT' ) . '.php documents/apstats/index.html --dir-scc=/snap/bin --dir-phpstan=~/git/phpstan/htdocs/includes/bin' ;
2023-08-23 14:24:52 +02:00
exit ( 0 );
}
$outputpath = $argv [ 1 ];
$outputdir = dirname ( $outputpath );
$outputfile = basename ( $outputpath );
2023-12-04 10:22:29 +01:00
if ( ! is_dir ( $outputdir )) {
2023-08-23 14:24:52 +02:00
print 'Error: dir ' . $outputdir . ' does not exists or is not writable' . " \n " ;
exit ( 1 );
}
$dirscc = '' ;
$dirphpstan = '' ;
$i = 0 ;
while ( $i < $argc ) {
$reg = array ();
if ( preg_match ( '/--dir-scc=(.*)$/' , $argv [ $i ], $reg )) {
2023-08-27 15:45:29 +02:00
$dirscc = $reg [ 1 ];
2023-08-23 14:24:52 +02:00
}
if ( preg_match ( '/--dir-phpstan=(.*)$/' , $argv [ $i ], $reg )) {
$dirphpstan = $reg [ 1 ];
}
$i ++ ;
}
2023-12-15 10:28:16 +01:00
$timestart = time ();
2023-08-23 14:24:52 +02:00
// Count lines of code of Dolibarr itself
/*
$commandcheck = 'cloc . --exclude-dir=includes --exclude-dir=custom --ignore-whitespace --vcs=git' ;
$resexec = shell_exec ( $commandcheck );
$resexec = ( int ) ( empty ( $resexec ) ? 0 : trim ( $resexec ));
// Count lines of code of external dependencies
$commandcheck = 'cloc htdocs/includes --ignore-whitespace --vcs=git' ;
$resexec = shell_exec ( $commandcheck );
$resexec = ( int ) ( empty ( $resexec ) ? 0 : trim ( $resexec ));
*/
2024-01-14 12:26:37 +01:00
// Retrieve the .git information
2024-01-13 15:20:28 +01:00
$urlgit = 'https://github.com/Dolibarr/dolibarr/blob/develop/' ;
2023-08-23 14:24:52 +02:00
// Count lines of code of application
2024-02-11 13:35:49 +01:00
if ( $dirscc != 'disabled' ) {
$commandcheck = ( $dirscc ? $dirscc . '/' : '' ) . 'scc . --exclude-dir=htdocs/includes,htdocs/custom,htdocs/theme/common/fontawesome-5,htdocs/theme/common/octicons' ;
print 'Execute SCC to count lines of code in project: ' . $commandcheck . " \n " ;
$output_arrproj = array ();
$resexecproj = 0 ;
exec ( $commandcheck , $output_arrproj , $resexecproj );
2023-08-23 14:24:52 +02:00
2024-02-11 13:35:49 +01:00
// Count lines of code of dependencies
$commandcheck = ( $dirscc ? $dirscc . '/' : '' ) . 'scc htdocs/includes htdocs/theme/common/fontawesome-5 htdocs/theme/common/octicons' ;
print 'Execute SCC to count lines of code in dependencies: ' . $commandcheck . " \n " ;
$output_arrdep = array ();
$resexecdep = 0 ;
exec ( $commandcheck , $output_arrdep , $resexecdep );
}
2023-08-23 14:24:52 +02:00
// Get technical debt
2024-02-11 13:35:49 +01:00
if ( $dirphpstan != 'disabled' ) {
$commandcheck = ( $dirphpstan ? $dirphpstan . '/' : '' ) . 'phpstan --level=' . $phpstanlevel . ' -v analyze -a build/phpstan/bootstrap.php --memory-limit 5G --error-format=github' ;
print 'Execute PHPStan to get the technical debt: ' . $commandcheck . " \n " ;
$output_arrtd = array ();
$resexectd = 0 ;
exec ( $commandcheck , $output_arrtd , $resexectd );
}
2023-08-23 14:24:52 +02:00
2024-01-13 13:12:21 +01:00
// Count lines of code of dependencies
2024-01-15 03:08:38 +01:00
$commandcheck = " git log --shortstat --no-renames --no-merges --use-mailmap --pretty='format:%cI;%H;%aN;%ae;%ce' " ; // --since= --until=...
print 'Execute git log to count number of commits by day: ' . $commandcheck . " \n " ;
2024-01-13 13:12:21 +01:00
$output_arrglpu = array ();
$resexecglpu = 0 ;
2024-01-15 03:15:27 +01:00
//exec($commandcheck, $output_arrglpu, $resexecglpu);
2024-01-13 13:12:21 +01:00
2023-08-23 14:24:52 +02:00
$arrayoflineofcode = array ();
$arraycocomo = array ();
2023-08-23 17:53:12 +02:00
$arrayofmetrics = array (
2023-12-04 10:22:29 +01:00
'proj' => array ( 'Bytes' => 0 , 'Files' => 0 , 'Lines' => 0 , 'Blanks' => 0 , 'Comments' => 0 , 'Code' => 0 , 'Complexity' => 0 ),
'dep' => array ( 'Bytes' => 0 , 'Files' => 0 , 'Lines' => 0 , 'Blanks' => 0 , 'Comments' => 0 , 'Code' => 0 , 'Complexity' => 0 )
2023-08-23 17:53:12 +02:00
);
2023-08-23 14:24:52 +02:00
// Analyse $output_arrproj
foreach ( array ( 'proj' , 'dep' ) as $source ) {
print 'Analyze SCC result for lines of code for ' . $source . " \n " ;
if ( $source == 'proj' ) {
$output_arr = & $output_arrproj ;
} elseif ( $source == 'dep' ) {
$output_arr = & $output_arrdep ;
} else {
print 'Bad value for $source' ;
die ();
}
foreach ( $output_arr as $line ) {
if ( preg_match ( '/^(───|Language|Total)/' , $line )) {
continue ;
}
//print $line."<br>\n";
if ( preg_match ( '/^Estimated Cost.*\$(.*)/i' , $line , $reg )) {
2023-08-23 18:26:27 +02:00
$arraycocomo [ $source ][ 'currency' ] = preg_replace ( '/[^\d\.]/' , '' , str_replace ( array ( ',' , ' ' ), array ( '' , '' ), $reg [ 1 ]));
2023-08-23 14:24:52 +02:00
}
if ( preg_match ( '/^Estimated Schedule Effort.*\s([\d\s,]+)/i' , $line , $reg )) {
$arraycocomo [ $source ][ 'effort' ] = str_replace ( array ( ',' , ' ' ), array ( '.' , '' ), $reg [ 1 ]);
}
if ( preg_match ( '/^Estimated People.*\s([\d\s,]+)/i' , $line , $reg )) {
$arraycocomo [ $source ][ 'people' ] = str_replace ( array ( ',' , ' ' ), array ( '.' , '' ), $reg [ 1 ]);
}
if ( preg_match ( '/^Processed\s(\d+)\s/i' , $line , $reg )) {
$arrayofmetrics [ $source ][ 'Bytes' ] = $reg [ 1 ];
}
if ( preg_match ( '/^(.*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/' , $line , $reg )) {
$arrayoflineofcode [ $source ][ $reg [ 1 ]][ 'Files' ] = $reg [ 2 ];
$arrayoflineofcode [ $source ][ $reg [ 1 ]][ 'Lines' ] = $reg [ 3 ];
$arrayoflineofcode [ $source ][ $reg [ 1 ]][ 'Blanks' ] = $reg [ 4 ];
$arrayoflineofcode [ $source ][ $reg [ 1 ]][ 'Comments' ] = $reg [ 5 ];
$arrayoflineofcode [ $source ][ $reg [ 1 ]][ 'Code' ] = $reg [ 6 ];
$arrayoflineofcode [ $source ][ $reg [ 1 ]][ 'Complexity' ] = $reg [ 7 ];
}
}
2023-08-23 17:53:12 +02:00
2023-08-26 10:16:46 +02:00
if ( ! empty ( $arrayoflineofcode [ $source ])) {
foreach ( $arrayoflineofcode [ $source ] as $key => $val ) {
$arrayofmetrics [ $source ][ 'Files' ] += $val [ 'Files' ];
$arrayofmetrics [ $source ][ 'Lines' ] += $val [ 'Lines' ];
$arrayofmetrics [ $source ][ 'Blanks' ] += $val [ 'Blanks' ];
$arrayofmetrics [ $source ][ 'Comments' ] += $val [ 'Comments' ];
$arrayofmetrics [ $source ][ 'Code' ] += $val [ 'Code' ];
$arrayofmetrics [ $source ][ 'Complexity' ] += $val [ 'Complexity' ];
}
2023-08-23 17:53:12 +02:00
}
2023-08-23 14:24:52 +02:00
}
2024-01-13 12:05:37 +01:00
// Search the max
2024-01-13 12:16:26 +01:00
$arrayofmax = array ( 'Lines' => 0 );
2024-01-13 12:05:37 +01:00
foreach ( array ( 'proj' , 'dep' ) as $source ) {
2024-02-11 13:35:49 +01:00
if ( ! empty ( $arrayoflineofcode [ $source ])) {
foreach ( $arrayoflineofcode [ $source ] as $val ) {
$arrayofmax [ 'Lines' ] = max ( $arrayofmax [ 'Lines' ], $val [ 'Lines' ]);
}
}
}
// Retrieve the .git information
2024-02-11 15:42:03 +01:00
$nbofmonth = 6 ;
$delay = ( 3600 * 24 * 30 * $nbofmonth );
2024-02-11 13:35:49 +01:00
$urlgit = 'https://api.github.com/search/issues?q=is:pr+repo:Dolibarr/dolibarr+created:>' . dol_print_date ( dol_now () - $delay , " %Y-%m " );
$arrayofalerts = array ();
$arrayofalerts1 = $arrayofalerts2 = $arrayofalerts3 = array ();
// Count lines of code of application
$newurl = $urlgit . '+CVE' ;
$result = getURLContent ( $newurl );
print 'Execute GET on github for ' . $newurl . " \n " ;
if ( $result && $result [ 'http_code' ] == 200 ) {
$arrayofalerts1 = json_decode ( $result [ 'content' ]);
foreach ( $arrayofalerts1 -> items as $val ) {
$tmpval = cleanVal ( $val );
if ( preg_match ( '/CVE/i' , $tmpval [ 'title' ])) {
$arrayofalerts [ $tmpval [ 'number' ]] = $tmpval ;
}
2024-01-13 12:05:37 +01:00
}
2024-02-11 13:35:49 +01:00
} else {
print 'Error: failed to get github response' ;
exit ( - 1 );
2024-01-13 12:05:37 +01:00
}
2024-02-11 13:35:49 +01:00
$newurl = $urlgit . '+yogosha' ;
$result = getURLContent ( $newurl );
print 'Execute GET on github for ' . $newurl . " \n " ;
if ( $result && $result [ 'http_code' ] == 200 ) {
$arrayofalerts2 = json_decode ( $result [ 'content' ]);
foreach ( $arrayofalerts2 -> items as $val ) {
$tmpval = cleanVal ( $val );
if ( preg_match ( '/yogosha:/i' , $tmpval [ 'title' ])) {
$arrayofalerts [ $tmpval [ 'number' ]] = $tmpval ;
}
}
} else {
print 'Error: failed to get github response' ;
exit ( - 1 );
}
$newurl = $urlgit . '+Sec:' ;
$result = getURLContent ( $newurl );
print 'Execute GET on github for ' . $newurl . " \n " ;
if ( $result && $result [ 'http_code' ] == 200 ) {
$arrayofalerts3 = json_decode ( $result [ 'content' ]);
foreach ( $arrayofalerts3 -> items as $val ) {
$tmpval = cleanVal ( $val );
if ( preg_match ( '/Sec:/i' , $tmpval [ 'title' ])) {
$arrayofalerts [ $tmpval [ 'number' ]] = $tmpval ;
}
}
} else {
print 'Error: failed to get github response' ;
exit ( - 1 );
}
2024-01-13 12:05:37 +01:00
2023-12-15 10:28:16 +01:00
$timeend = time ();
2023-08-23 14:24:52 +02:00
/*
* View
*/
2023-09-22 04:59:32 +02:00
$html = '<html>' . " \n " ;
$html .= '<meta charset="utf-8">' . " \n " ;
2023-11-19 10:38:15 +01:00
$html .= '<meta http-equiv="refresh" content="300">' . " \n " ;
2023-09-22 04:59:32 +02:00
$html .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">' . " \n " ;
2024-02-11 13:43:05 +01:00
$html .= '<meta name="keywords" content="erp, crm, dolibarr, statistic, projet, security alerts" />' . " \n " ;
$html .= '<meta name="title" content="Dolibarr project statistics" />' . " \n " ;
$html .= '<meta name="description" content="Statistics about the Dolibarr ERP CRM Open Source project (lines of code, contributions, security alerts, technical debt..." />' . " \n " ;
2023-09-22 04:59:32 +02:00
$html .= '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />' . " \n " ;
$html .= '<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js" integrity="sha512-3gJwYpMe3QewGELv8k/BX9vcqhryRdzRMxVfq6ngyWXwo03GFEzjsUm8Q7RZcHPHksttq7/GFoxjCVUjkjvPdw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>' . " \n " ;
2023-08-23 14:24:52 +02:00
$html .= '
< style >
body {
margin : 10 px ;
2023-08-23 17:53:12 +02:00
margin - left : 50 px ;
margin - right : 50 px ;
2023-08-23 14:24:52 +02:00
}
h1 {
font - size : 1.5 em ;
font - weight : bold ;
padding - top : 5 px ;
padding - bottom : 5 px ;
margin - top : 5 px ;
margin - bottom : 5 px ;
}
2023-08-23 17:53:12 +02:00
header {
text - align : center ;
}
2023-08-23 14:24:52 +02:00
header , section . chapter {
margin - top : 10 px ;
margin - bottom : 10 px ;
padding : 10 px ;
}
2023-08-23 17:53:12 +02:00
table {
border - collapse : collapse ;
}
th , td {
padding - top : 5 px ;
padding - bottom : 5 px ;
padding - left : 10 px ;
padding - right : 10 px ;
}
2023-08-23 14:24:52 +02:00
. left {
text - align : left ;
}
. right {
text - align : right ;
}
2023-09-22 04:59:32 +02:00
. nowrap {
white - space : nowrap ;
}
2023-12-15 11:18:04 +01:00
. opacitymedium {
2023-08-23 14:24:52 +02:00
opacity : 0.5 ;
}
2023-08-23 17:53:12 +02:00
. centpercent {
width : 100 % ;
}
2023-08-23 19:29:14 +02:00
. hidden {
display : none ;
}
2023-08-23 17:53:12 +02:00
. trgroup {
2024-01-13 11:48:54 +01:00
border - bottom : 1 px solid #aaa;
2023-08-23 17:53:12 +02:00
}
2023-09-01 19:38:47 +02:00
. seedetail {
color : #000088;
2023-12-15 14:21:51 +01:00
cursor : pointer ;
2023-09-01 19:38:47 +02:00
}
. box {
padding : 20 px ;
font - size : 1.2 em ;
margin - top : 10 px ;
2023-09-03 18:51:34 +02:00
margin - bottom : 10 px ;
width : 200 px ;
}
. box . inline - box {
display : inline - block ;
text - align : center ;
margin - left : 10 px ;
}
2024-01-13 11:40:04 +01:00
. boxallwidth {
border - radius : 9 px ;
border - color : #000;
border - width : 2 px ;
padding : 5 px ;
border - style : solid ;
2024-01-13 12:16:26 +01:00
background - color : #f8f8f8;
2024-01-13 11:40:04 +01:00
}
2023-09-03 18:51:34 +02:00
. back1 {
2023-09-05 22:41:17 +02:00
background - color : #884466;
2023-09-03 18:51:34 +02:00
color : #FFF;
}
2023-09-05 22:41:17 +02:00
. back2 {
background - color : #664488;
2023-09-03 18:51:34 +02:00
color : #FFF;
2023-09-01 19:38:47 +02:00
}
2023-09-22 04:59:32 +02:00
div . fiche > form > div . div - table - responsive {
min - height : 392 px ;
}
div . fiche > form > div . div - table - responsive , div . fiche > form > div . div - table - responsive - no - min {
overflow - x : auto ;
}
. div - table - responsive {
line - height : 120 % ;
}
. div - table - responsive , . div - table - responsive - no - min {
overflow - x : auto ;
min - height : 0.01 % ;
}
2024-01-13 11:40:04 +01:00
. list_technical_debt {
2024-01-13 15:20:28 +01:00
/* font-size: smaller */
2024-01-13 11:40:04 +01:00
}
. pictofixedwidth {
font - size : smaller ;
width : 28 px ;
vertical - align : middle ;
}
2024-01-13 11:54:47 +01:00
. bargraph {
2024-01-13 11:59:37 +01:00
background - color : #358;
}
. small {
font - size : smaller ;
2024-01-13 11:54:47 +01:00
}
2024-01-13 15:25:10 +01:00
. fr {
float : right ;
}
2023-09-22 04:59:32 +02:00
/* Force values for small screen 767 */
@ media only screen and ( max - width : 767 px )
{
body {
margin : 5 px ;
margin - left : 5 px ;
margin - right : 5 px ;
}
}
</ style > ' . " \n " ;
2023-08-23 14:24:52 +02:00
2023-09-22 04:36:44 +02:00
$html .= '<body>' . " \n " ;
2023-08-23 14:24:52 +02:00
2024-01-13 13:12:21 +01:00
// Header
2023-09-22 04:36:44 +02:00
$html .= '<header>' . " \n " ;
$html .= '<h1>Advanced Project Statistics</h1>' . " \n " ;
2023-08-23 14:24:52 +02:00
$currentDate = date ( " Y-m-d H:i:s " ); // Format: Year-Month-Day Hour:Minute:Second
2023-12-15 11:18:04 +01:00
$html .= '<span class="opacitymedium">Generated on ' . $currentDate . ' in ' . ( $timeend - $timestart ) . ' seconds</span>' . " \n " ;
2023-09-22 04:36:44 +02:00
$html .= '</header>' . " \n " ;
2023-08-23 14:24:52 +02:00
2024-01-13 13:12:21 +01:00
// Lines of code
2023-11-19 10:38:15 +01:00
$html .= '<section class="chapter" id="linesofcode">' . " \n " ;
2024-01-13 11:40:04 +01:00
$html .= '<h2><span class="fas fa-code pictofixedwidth"></span>Lines of code</h2>' . " \n " ;
2023-09-22 04:59:32 +02:00
$html .= '<div class="div-table-responsive">' . " \n " ;
2024-01-04 16:15:20 +01:00
$html .= '<div class="boxallwidth">' . " \n " ;
2023-08-23 17:53:12 +02:00
$html .= '<table class="centpercent">' ;
2023-08-23 14:24:52 +02:00
$html .= '<tr class="loc">' ;
2023-11-20 20:20:14 +01:00
$html .= '<th class="left">Language</th>' ;
2023-08-23 17:53:12 +02:00
$html .= '<th class="right">Bytes</th>' ;
2023-08-23 14:24:52 +02:00
$html .= '<th class="right">Files</th>' ;
$html .= '<th class="right">Lines</th>' ;
2024-01-13 11:46:43 +01:00
$html .= '<th></th>' ;
2023-08-23 14:24:52 +02:00
$html .= '<th class="right">Blanks</th>' ;
$html .= '<th class="right">Comments</th>' ;
$html .= '<th class="right">Code</th>' ;
//$html .= '<td class="right">'.$val['Complexity'].'</td>';
2023-11-20 20:20:14 +01:00
$html .= '</tr>' ;
2023-08-23 14:24:52 +02:00
foreach ( array ( 'proj' , 'dep' ) as $source ) {
2023-08-23 17:53:12 +02:00
$html .= '<tr class="trgroup" id="source' . $source . '">' ;
2023-08-23 14:24:52 +02:00
if ( $source == 'proj' ) {
2023-12-15 14:41:03 +01:00
$html .= '<td>All files without dependencies' ;
2023-08-23 14:24:52 +02:00
} elseif ( $source == 'dep' ) {
2023-12-15 14:41:03 +01:00
$html .= '<td>All files of dependencies only' ;
2023-08-23 14:24:52 +02:00
}
2024-01-13 15:27:04 +01:00
$html .= ' <div class="seedetail fr" data-source="' . $source . '"><span class="fas fa-chart-bar pictofixedwidth"></span>See detail per file type...</span>' ;
2023-08-23 19:29:14 +02:00
$html .= '<td class="right">' . formatNumber ( $arrayofmetrics [ $source ][ 'Bytes' ]) . '</td>' ;
$html .= '<td class="right">' . formatNumber ( $arrayofmetrics [ $source ][ 'Files' ]) . '</td>' ;
$html .= '<td class="right">' . formatNumber ( $arrayofmetrics [ $source ][ 'Lines' ]) . '</td>' ;
2024-01-13 11:46:43 +01:00
$html .= '<td></td>' ;
2023-08-23 19:29:14 +02:00
$html .= '<td class="right">' . formatNumber ( $arrayofmetrics [ $source ][ 'Blanks' ]) . '</td>' ;
$html .= '<td class="right">' . formatNumber ( $arrayofmetrics [ $source ][ 'Comments' ]) . '</td>' ;
$html .= '<td class="right">' . formatNumber ( $arrayofmetrics [ $source ][ 'Code' ]) . '</td>' ;
2024-01-13 11:46:43 +01:00
//$html .= '<td></td>';
2023-08-23 17:53:12 +02:00
$html .= '</tr>' ;
2023-08-26 10:16:46 +02:00
if ( ! empty ( $arrayoflineofcode [ $source ])) {
foreach ( $arrayoflineofcode [ $source ] as $key => $val ) {
$html .= '<tr class="loc hidden source' . $source . ' language' . str_replace ( ' ' , '' , $key ) . '">' ;
$html .= '<td>' . $key . '</td>' ;
$html .= '<td class="right"></td>' ;
2023-09-22 04:59:32 +02:00
$html .= '<td class="right nowrap">' . ( empty ( $val [ 'Files' ]) ? '' : formatNumber ( $val [ 'Files' ])) . '</td>' ;
$html .= '<td class="right nowrap">' . ( empty ( $val [ 'Lines' ]) ? '' : formatNumber ( $val [ 'Lines' ])) . '</td>' ;
2024-01-13 11:46:43 +01:00
$html .= '<td class="nowrap">' ;
2024-01-13 12:16:26 +01:00
$percent = $val [ 'Lines' ] / $arrayofmax [ 'Lines' ];
2024-01-13 11:54:47 +01:00
$widthbar = round ( 200 * $percent );
2024-01-13 12:05:37 +01:00
$html .= '<div class="bargraph" style="width: ' . max ( 1 , $widthbar ) . 'px"> </div>' ;
2024-01-13 11:46:43 +01:00
$html .= '</td>' ;
2023-09-22 04:59:32 +02:00
$html .= '<td class="right nowrap">' . ( empty ( $val [ 'Blanks' ]) ? '' : formatNumber ( $val [ 'Blanks' ])) . '</td>' ;
$html .= '<td class="right nowrap">' . ( empty ( $val [ 'Comments' ]) ? '' : formatNumber ( $val [ 'Comments' ])) . '</td>' ;
$html .= '<td class="right nowrap">' . ( empty ( $val [ 'Code' ]) ? '' : formatNumber ( $val [ 'Code' ])) . '</td>' ;
2023-08-26 10:16:46 +02:00
//$html .= '<td class="right">'.(empty($val['Complexity']) ? '' : $val['Complexity']).'</td>';
2024-01-13 11:46:43 +01:00
/* $html .= '<td class="nowrap">' ;
$html .= '' ;
$html .= '</td>' ;
*/
2023-08-26 10:16:46 +02:00
$html .= '</tr>' ;
}
2023-08-23 14:24:52 +02:00
}
}
2024-01-13 11:46:43 +01:00
$html .= '<tr class="trgrouptotal">' ;
2023-08-23 17:53:12 +02:00
$html .= '<td class="left">Total</td>' ;
2023-09-22 04:59:32 +02:00
$html .= '<td class="right nowrap">' . formatNumber ( $arrayofmetrics [ 'proj' ][ 'Bytes' ] + $arrayofmetrics [ 'dep' ][ 'Bytes' ]) . '</td>' ;
$html .= '<td class="right nowrap">' . formatNumber ( $arrayofmetrics [ 'proj' ][ 'Files' ] + $arrayofmetrics [ 'dep' ][ 'Files' ]) . '</td>' ;
$html .= '<td class="right nowrap">' . formatNumber ( $arrayofmetrics [ 'proj' ][ 'Lines' ] + $arrayofmetrics [ 'dep' ][ 'Lines' ]) . '</td>' ;
2024-01-13 11:48:54 +01:00
$html .= '<td></td>' ;
2023-09-22 04:59:32 +02:00
$html .= '<td class="right nowrap">' . formatNumber ( $arrayofmetrics [ 'proj' ][ 'Blanks' ] + $arrayofmetrics [ 'dep' ][ 'Blanks' ]) . '</td>' ;
$html .= '<td class="right nowrap">' . formatNumber ( $arrayofmetrics [ 'proj' ][ 'Comments' ] + $arrayofmetrics [ 'dep' ][ 'Comments' ]) . '</td>' ;
$html .= '<td class="right nowrap">' . formatNumber ( $arrayofmetrics [ 'proj' ][ 'Code' ] + $arrayofmetrics [ 'dep' ][ 'Code' ]) . '</td>' ;
2023-08-23 14:24:52 +02:00
//$html .= '<td>'.$arrayofmetrics['Complexity'].'</td>';
2024-01-13 11:48:54 +01:00
//$html .= '<td></td>';
2023-08-23 14:24:52 +02:00
$html .= '</tr>' ;
2023-08-23 17:53:12 +02:00
$html .= '</table>' ;
2023-09-22 04:59:32 +02:00
$html .= '</div>' ;
2024-01-04 16:15:20 +01:00
$html .= '</div>' ;
2023-08-23 14:24:52 +02:00
2023-09-22 04:36:44 +02:00
$html .= '</section>' . " \n " ;
2023-08-23 14:24:52 +02:00
2024-01-13 13:12:21 +01:00
// Contributions
$html .= '<section class="chapter" id="projectvalue">' . " \n " ;
2024-01-13 14:57:08 +01:00
$html .= '<h2><span class="fas fa-tasks pictofixedwidth"></span>Contributions</h2>' . " \n " ;
2024-01-13 13:12:21 +01:00
$html .= '<div class="boxallwidth">' . " \n " ;
2024-01-13 14:55:21 +01:00
$html .= 'TODO...' ;
$html .= '<!-- ' ;
2024-01-13 14:31:31 +01:00
foreach ( $output_arrglpu as $line ) {
$html .= $line . " \n " ;
}
2024-01-13 14:55:21 +01:00
$html .= ' -->' ;
2024-01-13 13:12:21 +01:00
$html .= '</div>' ;
$html .= '</section>' . " \n " ;
2024-01-13 14:57:08 +01:00
// Contributors
2024-01-13 13:12:21 +01:00
$html .= '<section class="chapter" id="projectvalue">' . " \n " ;
2024-01-13 14:57:08 +01:00
$html .= '<h2><span class="fas fa-user pictofixedwidth"></span>Contributors</h2>' . " \n " ;
2024-01-13 13:12:21 +01:00
$html .= '<div class="boxallwidth">' . " \n " ;
$html .= 'TODO...' ;
$html .= '</div>' ;
$html .= '</section>' . " \n " ;
// Project value
2023-11-19 10:38:15 +01:00
$html .= '<section class="chapter" id="projectvalue">' . " \n " ;
2024-01-13 12:18:32 +01:00
$html .= '<h2><span class="fas fa-dollar-sign pictofixedwidth"></span>Project value</h2>' . " \n " ;
2024-01-04 16:15:20 +01:00
$html .= '<div class="boxallwidth">' . " \n " ;
2023-09-03 18:51:34 +02:00
$html .= '<div class="box inline-box back1">' ;
2024-01-13 11:59:37 +01:00
$html .= 'COCOMO value<br><span class="small opacitymedium">(Basic organic model)</span><br>' ;
2023-09-22 04:36:44 +02:00
$html .= '<b>$' . formatNumber (( empty ( $arraycocomo [ 'proj' ][ 'currency' ]) ? 0 : $arraycocomo [ 'proj' ][ 'currency' ]) + ( empty ( $arraycocomo [ 'dep' ][ 'currency' ]) ? 0 : $arraycocomo [ 'dep' ][ 'currency' ]), 2 ) . '</b>' ;
2023-09-01 19:38:47 +02:00
$html .= '</div>' ;
2023-09-03 18:51:34 +02:00
$html .= '<div class="box inline-box back2">' ;
2024-01-13 11:59:37 +01:00
$html .= 'COCOMO effort<br><span class="small opacitymedium">(Basic organic model)</span><br>' ;
2023-09-03 18:51:34 +02:00
$html .= '<b>' . formatNumber ( $arraycocomo [ 'proj' ][ 'people' ] * $arraycocomo [ 'proj' ][ 'effort' ] + $arraycocomo [ 'dep' ][ 'people' ] * $arraycocomo [ 'dep' ][ 'effort' ]);
2024-01-12 17:14:13 +01:00
$html .= ' months people</b>' ;
2024-01-04 16:15:20 +01:00
$html .= '</div>' ;
$html .= '</div>' ;
2023-09-22 04:36:44 +02:00
$html .= '</section>' . " \n " ;
2023-12-15 10:28:16 +01:00
$tmp = '' ;
$nblines = 0 ;
2024-02-11 13:35:49 +01:00
if ( ! empty ( $output_arrtd )) {
foreach ( $output_arrtd as $line ) {
$reg = array ();
//print $line."\n";
preg_match ( '/^::error file=(.*),line=(\d+),col=(\d+)::(.*)$/' , $line , $reg );
if ( ! empty ( $reg [ 1 ])) {
if ( $nblines < 20 ) {
$tmp .= '<tr class="nohidden">' ;
} else {
$tmp .= '<tr class="hidden sourcephpstan">' ;
}
$tmp .= '<td>' . $reg [ 1 ] . '</td>' ;
$tmp .= '<td class="">' ;
$tmp .= '<a href="' . $urlgit . $reg [ 1 ] . '#L' . $reg [ 2 ] . '" target="_blank">' . $reg [ 2 ] . '</a>' ;
$tmp .= '</td>' ;
$tmp .= '<td>' . $reg [ 4 ] . '</td>' ;
$tmp .= '</tr>' . " \n " ;
$nblines ++ ;
2024-01-13 15:06:49 +01:00
}
2023-09-22 04:36:44 +02:00
}
}
2023-12-15 10:28:16 +01:00
2024-01-13 13:12:21 +01:00
2024-02-11 13:35:49 +01:00
// Last security errors
$html .= '<section class="chapter" id="linesofcode">' . " \n " ;
2024-02-11 15:42:03 +01:00
$html .= '<h2><span class="fas fa-code pictofixedwidth"></span>Last security alerts <span class="opacitymedium">(last ' . $nbofmonth . ' month)</span></h2>' . " \n " ;
2024-02-11 13:35:49 +01:00
$html .= '<div class="div-table-responsive">' . " \n " ;
$html .= '<div class="boxallwidth">' . " \n " ;
$html .= '<table class="list_technical_debt centpercent">' . " \n " ;
$html .= '<tr class="trgroup"><td>ID</td><td>Title</td><td>Date</td></tr>' . " \n " ;
foreach ( $arrayofalerts as $alert ) {
2024-02-11 13:44:27 +01:00
$html .= '<tr><td><a href="https://github.com/Dolibarr/dolibarr/issues/' . $alert [ 'number' ] . '">#' . $alert [ 'number' ] . '</a></td>' ;
2024-02-11 13:35:49 +01:00
$html .= '<td>' . $alert [ 'title' ] . '</td><td>' ;
$html .= $alert [ 'created_at' ] . '</td>' ;
$html .= '</tr>' ;
}
$html .= '</table>' ;
$html .= '</div>' ;
$html .= '</div>' ;
$html .= '</section>' ;
2024-01-13 13:12:21 +01:00
// Technical debt
2023-12-15 10:28:16 +01:00
$html .= '<section class="chapter" id="technicaldebt">' . " \n " ;
2024-01-13 12:18:32 +01:00
$html .= '<h2><span class="fas fa-book-dead pictofixedwidth"></span>Technical debt <span class="opacitymedium">(PHPStan level ' . $phpstanlevel . ' -> ' . $nblines . ' warnings)</span></h2>' . " \n " ;
2024-01-04 16:15:20 +01:00
2023-12-15 10:28:16 +01:00
$html .= '<div class="div-table-responsive">' . " \n " ;
2024-01-04 16:15:20 +01:00
$html .= '<div class="boxallwidth">' . " \n " ;
2024-01-13 15:20:28 +01:00
$html .= '<table class="list_technical_debt centpercent">' . " \n " ;
2024-01-13 11:59:37 +01:00
$html .= '<tr class="trgroup"><td>File</td><td>Line</td><td>Type</td></tr>' . " \n " ;
2023-12-15 10:28:16 +01:00
$html .= $tmp ;
2024-01-13 15:13:00 +01:00
$html .= '<tr class="sourcephpstan"><td colspan="3"><span class="seedetail" data-source="phpstan" id="sourcephpstan">Show all...</span></td></tr>' ;
2023-09-22 04:36:44 +02:00
$html .= '</table>' ;
$html .= '</div>' ;
2024-01-04 16:15:20 +01:00
$html .= '</div>' ;
2023-09-22 04:36:44 +02:00
$html .= '</section>' . " \n " ;
2023-08-23 14:24:52 +02:00
2024-01-13 13:12:21 +01:00
2024-02-11 13:35:49 +01:00
// JS code to allow to expand/collapse
2024-01-13 13:12:21 +01:00
2023-08-23 19:29:14 +02:00
$html .= '
< script >
$ ( document ) . ready ( function () {
2024-01-13 15:06:49 +01:00
$ ( " .seedetail " ) . on ( " click " , function () {
2023-08-23 19:29:14 +02:00
var source = $ ( this ) . attr ( " data-source " );
2024-01-13 15:09:15 +01:00
console . log ( " Click on " + source + " so we show class .source " + source );
2023-08-23 19:29:14 +02:00
jQuery ( " .source " + source ) . toggle ();
} );
});
</ script >
' ;
2023-11-20 20:20:14 +01:00
$html .= '</body>' ;
2023-08-23 14:24:52 +02:00
$html .= '</html>' ;
2024-01-13 13:12:21 +01:00
// Output report into a HTML file
2023-08-23 14:24:52 +02:00
$fh = fopen ( $outputpath , 'w' );
if ( $fh ) {
fwrite ( $fh , $html );
fclose ( $fh );
print 'Generation of output file ' . $outputfile . ' done.' . " \n " ;
} else {
2024-01-12 17:14:13 +01:00
print 'Failed to open ' . $outputfile . ' for output.' . " \n " ;
2023-08-23 14:24:52 +02:00
}
2023-08-23 19:29:14 +02:00
/**
* function to format a number
*
* @ param string | int $number Number to format
* @ param int $nbdec Number of decimal digits
2024-01-12 17:14:13 +01:00
* @ return string Formatted string
2023-08-23 19:29:14 +02:00
*/
function formatNumber ( $number , $nbdec = 0 )
{
return number_format ( $number , 0 , '.' , ' ' );
}
2024-02-11 13:35:49 +01:00
/**
* cleanVal
*
* @ param array $val Array of a PR
* @ return Array of a PR
*/
function cleanVal ( $val )
{
$tmpval = array ();
$tmpval [ 'url' ] = $val -> url ;
$tmpval [ 'number' ] = $val -> number ;
$tmpval [ 'title' ] = $val -> title ;
$tmpval [ 'created_at' ] = $val -> created_at ;
$tmpval [ 'updated_at' ] = $val -> updated_at ;
return $tmpval ;
}