@@ -23,18 +23,22 @@ func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | |||
if repo.HasWiki() { | |||
wikiPath = repo.WikiPath() | |||
} | |||
return getRepoKPIStats(repo.RepoPath(), wikiPath) | |||
repoCreated := time.Unix(int64(repo.CreatedUnix), 0) | |||
return getRepoKPIStats(repo.RepoPath(), repoCreated, wikiPath) | |||
} | |||
func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error) { | |||
func getRepoKPIStats(repoPath string, repoCreated time.Time, wikiPath string) (*git.RepoKPIStats, error) { | |||
stats := &git.RepoKPIStats{} | |||
contributors, err := git.GetContributors(repoPath) | |||
contributors, err := git.GetContributorsDetail(repoPath, repoCreated) | |||
if err != nil { | |||
return nil, err | |||
} | |||
timeUntil := time.Now() | |||
fourMonthAgo := timeUntil.AddDate(0, -4, 0) | |||
if fourMonthAgo.Before(repoCreated) { | |||
fourMonthAgo = repoCreated | |||
} | |||
recentlyContributors, err := git.GetContributorsDetail(repoPath, fourMonthAgo) | |||
newContributersDict := make(map[string]struct{}) | |||
if err != nil { | |||
@@ -44,7 +48,7 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||
if contributors != nil { | |||
contributorDistinctDict := make(map[string]int, 0) | |||
keyContributorsDict := make(map[string]struct{}, 0) | |||
var commitsCount int64 | |||
for _, contributor := range contributors { | |||
if strings.Compare(contributor.Email, "") == 0 { | |||
continue | |||
@@ -70,6 +74,8 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||
setKeyContributerDict(contributorDistinctDict, contributor.Email, keyContributorsDict) | |||
} | |||
commitsCount += int64(contributor.CommitCnt) | |||
} | |||
if recentlyContributors != nil { | |||
@@ -110,10 +116,10 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||
stats.Contributors = int64(len(contributorDistinctDict)) | |||
stats.KeyContributors = int64(len(keyContributorsDict)) | |||
stats.Commits = int64(commitsCount) | |||
} | |||
err = git.SetDevelopAge(repoPath, stats) | |||
err = git.SetDevelopAge(repoPath, stats, repoCreated) | |||
if err != nil { | |||
return nil, fmt.Errorf("FillFromGit: %v", err) | |||
} | |||
@@ -14,6 +14,7 @@ type RepoStatistic struct { | |||
Name string `xorm:"INDEX" json:"name"` | |||
OwnerName string `json:"ownerName"` | |||
IsPrivate bool `json:"isPrivate"` | |||
IsMirror bool `json:"isMirror"` | |||
Date string `xorm:"unique(s) NOT NULL" json:"date"` | |||
NumWatches int64 `xorm:"NOT NULL DEFAULT 0" json:"watch"` | |||
NumWatchesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
@@ -18,6 +18,7 @@ type RepoKPIStats struct { | |||
KeyContributors int64 | |||
DevelopAge int64 | |||
ContributorsAdded int64 | |||
Commits int64 | |||
CommitsAdded int64 | |||
CommitLinesModified int64 | |||
WikiPages int64 | |||
@@ -35,8 +36,9 @@ type UserKPITypeStats struct { | |||
isNewContributor bool //是否是4个月内的新增贡献者 | |||
} | |||
func SetDevelopAge(repoPath string, stats *RepoKPIStats) error { | |||
args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short"} | |||
func SetDevelopAge(repoPath string, stats *RepoKPIStats, fromTime time.Time) error { | |||
since := fromTime.Format(time.RFC3339) | |||
args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short", fmt.Sprintf("--since='%s'", since)} | |||
stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | |||
if err != nil { | |||
return err | |||
@@ -547,6 +547,7 @@ var ( | |||
GrowthCommit float64 | |||
GrowthComments float64 | |||
RecordBeginTime string | |||
IgnoreMirrorRepo bool | |||
}{} | |||
Warn_Notify_Mails []string | |||
@@ -1333,6 +1334,7 @@ func SetRadarMapConfig() { | |||
RadarMap.GrowthCommit = sec.Key("growth_commit").MustFloat64(0.2) | |||
RadarMap.GrowthComments = sec.Key("growth_comments").MustFloat64(0.2) | |||
RadarMap.RecordBeginTime = sec.Key("record_beigin_time").MustString("2021-11-05") | |||
RadarMap.IgnoreMirrorRepo = sec.Key("ignore_mirror_repo").MustBool(true) | |||
} | |||
@@ -229,7 +229,7 @@ organizations=组织 | |||
images = 云脑镜像 | |||
search=搜索 | |||
code=代码 | |||
data_analysis=数字看板 | |||
data_analysis=数字看板(内测) | |||
repo_no_results=未找到匹配的项目。 | |||
dataset_no_results = 未找到匹配的数据集。 | |||
user_no_results=未找到匹配的用户。 | |||
@@ -49,9 +49,11 @@ func RepoStatisticDaily(date string) { | |||
var minRepoRadar models.RepoStatistic | |||
var maxRepoRadar models.RepoStatistic | |||
for i, repo := range repos { | |||
isInitMinMaxRadar := false | |||
for _, repo := range repos { | |||
log.Info("start statistic: %s", getDistinctProjectName(repo)) | |||
var numDevMonths, numWikiViews, numContributor, numKeyContributor, numCommitsGrowth, numCommitLinesGrowth, numContributorsGrowth int64 | |||
var numDevMonths, numWikiViews, numContributor, numKeyContributor, numCommitsGrowth, numCommitLinesGrowth, numContributorsGrowth, numCommits int64 | |||
repoGitStat, err := models.GetRepoKPIStats(repo) | |||
if err != nil { | |||
log.Error("GetRepoKPIStats failed: %s", getDistinctProjectName(repo)) | |||
@@ -63,6 +65,8 @@ func RepoStatisticDaily(date string) { | |||
numCommitsGrowth = repoGitStat.CommitsAdded | |||
numCommitLinesGrowth = repoGitStat.CommitLinesModified | |||
numContributorsGrowth = repoGitStat.ContributorsAdded | |||
numCommits = repoGitStat.Commits | |||
} | |||
var issueFixedRate float32 | |||
@@ -98,15 +102,15 @@ func RepoStatisticDaily(date string) { | |||
} | |||
repoStat := models.RepoStatistic{ | |||
RepoID: repo.ID, | |||
Date: date, | |||
Name: repo.Name, | |||
IsPrivate: repo.IsPrivate, | |||
OwnerName: repo.OwnerName, | |||
NumWatches: int64(repo.NumWatches), | |||
NumStars: int64(repo.NumStars), | |||
NumForks: int64(repo.NumForks), | |||
RepoID: repo.ID, | |||
Date: date, | |||
Name: repo.Name, | |||
IsPrivate: repo.IsPrivate, | |||
IsMirror: repo.IsMirror, | |||
OwnerName: repo.OwnerName, | |||
NumWatches: int64(repo.NumWatches), | |||
NumStars: int64(repo.NumStars), | |||
NumForks: int64(repo.NumForks), | |||
NumDownloads: repo.CloneCnt, | |||
NumComments: numComments, | |||
NumVisits: int64(numVisits), | |||
@@ -117,7 +121,7 @@ func RepoStatisticDaily(date string) { | |||
DatasetSize: datasetSize, | |||
NumModels: 0, | |||
NumWikiViews: numWikiViews, | |||
NumCommits: repo.NumCommit, | |||
NumCommits: numCommits, | |||
NumIssues: int64(repo.NumIssues), | |||
NumPulls: int64(repo.NumPulls), | |||
IssueFixedRate: issueFixedRate, | |||
@@ -167,6 +171,7 @@ func RepoStatisticDaily(date string) { | |||
tempRepoStat := models.RepoStatistic{ | |||
RepoID: repoStat.RepoID, | |||
Date: repoStat.Date, | |||
IsMirror: repoStat.IsMirror, | |||
Impact: normalization.GetImpactInitValue(repoStat.NumWatches, repoStat.NumStars, repoStat.NumForks, repoStat.NumDownloads, repoStat.NumComments, repoStat.NumVisits), | |||
Completeness: normalization.GetCompleteInitValue(repoStat.NumClosedIssues, repoStat.NumVersions, repoStat.NumDevMonths, repoStat.DatasetSize, repoStat.NumModels, repoStat.NumWikiViews), | |||
Liveness: normalization.GetLivenessInitValue(repoStat.NumCommits, repoStat.NumIssues, repoStat.NumPulls, repoStat.NumVisits), | |||
@@ -177,57 +182,64 @@ func RepoStatisticDaily(date string) { | |||
reposRadar = append(reposRadar, &tempRepoStat) | |||
if i == 0 { | |||
minRepoRadar = tempRepoStat | |||
maxRepoRadar = tempRepoStat | |||
} else { | |||
if !isInitMinMaxRadar { | |||
if tempRepoStat.Impact < minRepoRadar.Impact { | |||
minRepoRadar.Impact = tempRepoStat.Impact | |||
if !setting.RadarMap.IgnoreMirrorRepo || (setting.RadarMap.IgnoreMirrorRepo && !tempRepoStat.IsMirror) { | |||
minRepoRadar = tempRepoStat | |||
maxRepoRadar = tempRepoStat | |||
isInitMinMaxRadar = true | |||
} | |||
if tempRepoStat.Impact > maxRepoRadar.Impact { | |||
maxRepoRadar.Impact = tempRepoStat.Impact | |||
} | |||
} else { | |||
if !setting.RadarMap.IgnoreMirrorRepo || (setting.RadarMap.IgnoreMirrorRepo && !tempRepoStat.IsMirror) { | |||
if tempRepoStat.Impact < minRepoRadar.Impact { | |||
minRepoRadar.Impact = tempRepoStat.Impact | |||
} | |||
if tempRepoStat.Completeness < minRepoRadar.Completeness { | |||
minRepoRadar.Completeness = tempRepoStat.Completeness | |||
} | |||
if tempRepoStat.Impact > maxRepoRadar.Impact { | |||
maxRepoRadar.Impact = tempRepoStat.Impact | |||
} | |||
if tempRepoStat.Completeness > maxRepoRadar.Completeness { | |||
maxRepoRadar.Completeness = tempRepoStat.Completeness | |||
} | |||
if tempRepoStat.Completeness < minRepoRadar.Completeness { | |||
minRepoRadar.Completeness = tempRepoStat.Completeness | |||
} | |||
if tempRepoStat.Liveness < minRepoRadar.Completeness { | |||
minRepoRadar.Liveness = tempRepoStat.Liveness | |||
} | |||
if tempRepoStat.Completeness > maxRepoRadar.Completeness { | |||
maxRepoRadar.Completeness = tempRepoStat.Completeness | |||
} | |||
if tempRepoStat.Liveness > maxRepoRadar.Liveness { | |||
maxRepoRadar.Liveness = tempRepoStat.Liveness | |||
} | |||
if tempRepoStat.Liveness < minRepoRadar.Completeness { | |||
minRepoRadar.Liveness = tempRepoStat.Liveness | |||
} | |||
if tempRepoStat.ProjectHealth < minRepoRadar.ProjectHealth { | |||
minRepoRadar.ProjectHealth = tempRepoStat.ProjectHealth | |||
} | |||
if tempRepoStat.Liveness > maxRepoRadar.Liveness { | |||
maxRepoRadar.Liveness = tempRepoStat.Liveness | |||
} | |||
if tempRepoStat.ProjectHealth > maxRepoRadar.ProjectHealth { | |||
maxRepoRadar.ProjectHealth = tempRepoStat.ProjectHealth | |||
} | |||
if tempRepoStat.ProjectHealth < minRepoRadar.ProjectHealth { | |||
minRepoRadar.ProjectHealth = tempRepoStat.ProjectHealth | |||
} | |||
if tempRepoStat.TeamHealth < minRepoRadar.TeamHealth { | |||
minRepoRadar.TeamHealth = tempRepoStat.TeamHealth | |||
} | |||
if tempRepoStat.ProjectHealth > maxRepoRadar.ProjectHealth { | |||
maxRepoRadar.ProjectHealth = tempRepoStat.ProjectHealth | |||
} | |||
if tempRepoStat.TeamHealth > maxRepoRadar.TeamHealth { | |||
maxRepoRadar.TeamHealth = tempRepoStat.TeamHealth | |||
} | |||
if tempRepoStat.TeamHealth < minRepoRadar.TeamHealth { | |||
minRepoRadar.TeamHealth = tempRepoStat.TeamHealth | |||
} | |||
if tempRepoStat.Growth < minRepoRadar.Growth { | |||
minRepoRadar.Growth = tempRepoStat.Growth | |||
} | |||
if tempRepoStat.TeamHealth > maxRepoRadar.TeamHealth { | |||
maxRepoRadar.TeamHealth = tempRepoStat.TeamHealth | |||
} | |||
if tempRepoStat.Growth < minRepoRadar.Growth { | |||
minRepoRadar.Growth = tempRepoStat.Growth | |||
} | |||
if tempRepoStat.Growth > maxRepoRadar.Growth { | |||
maxRepoRadar.Growth = tempRepoStat.Growth | |||
} | |||
if tempRepoStat.Growth > maxRepoRadar.Growth { | |||
maxRepoRadar.Growth = tempRepoStat.Growth | |||
} | |||
} | |||
@@ -238,13 +250,23 @@ func RepoStatisticDaily(date string) { | |||
//radar map | |||
log.Info("begin statistic radar") | |||
for _, radarInit := range reposRadar { | |||
radarInit.Impact = normalization.Normalization(radarInit.Impact, minRepoRadar.Impact, maxRepoRadar.Impact) | |||
radarInit.Completeness = normalization.Normalization(radarInit.Completeness, minRepoRadar.Completeness, maxRepoRadar.Completeness) | |||
radarInit.Liveness = normalization.Normalization(radarInit.Liveness, minRepoRadar.Liveness, maxRepoRadar.Liveness) | |||
radarInit.ProjectHealth = normalization.Normalization(radarInit.ProjectHealth, minRepoRadar.ProjectHealth, maxRepoRadar.ProjectHealth) | |||
radarInit.TeamHealth = normalization.Normalization(radarInit.TeamHealth, minRepoRadar.TeamHealth, maxRepoRadar.TeamHealth) | |||
radarInit.Growth = normalization.Normalization(radarInit.Growth, minRepoRadar.Growth, maxRepoRadar.Growth) | |||
radarInit.RadarTotal = normalization.GetRadarValue(radarInit.Impact, radarInit.Completeness, radarInit.Liveness, radarInit.ProjectHealth, radarInit.TeamHealth, radarInit.Growth) | |||
if radarInit.IsMirror && setting.RadarMap.IgnoreMirrorRepo { | |||
radarInit.Impact = 0 | |||
radarInit.Completeness = 0 | |||
radarInit.Liveness = 0 | |||
radarInit.ProjectHealth = 0 | |||
radarInit.TeamHealth = 0 | |||
radarInit.Growth = 0 | |||
radarInit.RadarTotal = 0 | |||
} else { | |||
radarInit.Impact = normalization.Normalization(radarInit.Impact, minRepoRadar.Impact, maxRepoRadar.Impact) | |||
radarInit.Completeness = normalization.Normalization(radarInit.Completeness, minRepoRadar.Completeness, maxRepoRadar.Completeness) | |||
radarInit.Liveness = normalization.Normalization(radarInit.Liveness, minRepoRadar.Liveness, maxRepoRadar.Liveness) | |||
radarInit.ProjectHealth = normalization.Normalization(radarInit.ProjectHealth, minRepoRadar.ProjectHealth, maxRepoRadar.ProjectHealth) | |||
radarInit.TeamHealth = normalization.Normalization(radarInit.TeamHealth, minRepoRadar.TeamHealth, maxRepoRadar.TeamHealth) | |||
radarInit.Growth = normalization.Normalization(radarInit.Growth, minRepoRadar.Growth, maxRepoRadar.Growth) | |||
radarInit.RadarTotal = normalization.GetRadarValue(radarInit.Impact, radarInit.Completeness, radarInit.Liveness, radarInit.ProjectHealth, radarInit.TeamHealth, radarInit.Growth) | |||
} | |||
models.UpdateRepoStat(radarInit) | |||
} | |||
@@ -53,7 +53,7 @@ | |||
:cell-style='cellStyle'> | |||
<el-table-column | |||
label="ID" | |||
align="center" | |||
align="left" | |||
prop="repo_id" | |||
stripe | |||
> | |||
@@ -61,9 +61,9 @@ | |||
<el-table-column | |||
label="项目名称" | |||
width="125px" | |||
align="center" | |||
align="left" | |||
prop="name" | |||
style="color:#0366D6 100%;" | |||
style="color:#0366D6;font-family: Roboto" | |||
> | |||
<template slot-scope="scope"> | |||
<a @click=goToDetailPage(scope.row.repo_id,scope.row.name,scope.row.ownerName)>{{scope.row.name}} </a> | |||
@@ -197,7 +197,7 @@ | |||
<el-row :gutter="20"> | |||
<el-col :span=18 > | |||
<div class="item_l" id="charts"> | |||
<div style="font-size:14px;color:#409eff;margin:20px 5px;">OpenI指数:{{tableDataIDTotal.openi | rounding}}</div> | |||
<div style="font-size:14px;color:#0366D6;margin:20px 5px;">OpenI指数:{{tableDataIDTotal.openi | rounding}}</div> | |||
<div > | |||
<el-col :span='8' id="radar_openi" :style="{ height: '300px'}"></el-col> | |||
@@ -208,7 +208,7 @@ | |||
<el-col :span=6 > | |||
<div class="item_r"> | |||
<div style="margin:0 5px;"> | |||
<div style="font-size:14px;color:rgb(0,0,0);margin:10px 0px">贡献者TOP10</div> | |||
<div style="font-size:14px;color:rgb(0,0,0);margin:10px 5px">贡献者TOP10</div> | |||
<div> | |||
<el-table | |||
:data="tableDataContTop10" | |||
@@ -237,6 +237,7 @@ | |||
<el-table-column | |||
prop="pr" | |||
label="PR" | |||
width="50px" | |||
align="center"> | |||
</el-table-column> | |||
<el-table-column | |||
@@ -825,14 +826,14 @@ | |||
type: 'radar', | |||
lineStyle:{ | |||
width:2, | |||
color: '#409effd6', | |||
color: '#0366D6', | |||
normal:{ | |||
color:'#409effd6 ' | |||
color:'#0366D6 ' | |||
} | |||
}, | |||
itemStyle : { | |||
normal : { | |||
color:'#409effd6' | |||
color:'#0366D6' | |||
} | |||
}, | |||
data: [{ | |||
@@ -914,12 +915,12 @@ | |||
lineStyle:{ | |||
width:1, | |||
normal:{ | |||
color:'#409effd6' | |||
color:'#0366D6' | |||
} | |||
}, | |||
itemStyle : { | |||
normal : { | |||
color:'#409effd6' | |||
color:'#0366D6' | |||
} | |||
}, | |||
} | |||
@@ -1023,6 +1024,7 @@ | |||
] | |||
}; | |||
// this.echartsSelectData.resize() | |||
this.echartsSelectData.setOption(this.option) | |||
// setTimeout(function (){ | |||
// window.onresize = function () { | |||
@@ -1189,10 +1191,10 @@ | |||
.btnFirst{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px 0px 0px 4px; | |||
@@ -1200,20 +1202,20 @@ | |||
.btn{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
} | |||
.btnLast{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
/* border-right: none; */ | |||
background: #FFFF; | |||
color: #409eff; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:0px 4px 4px 0px; | |||
@@ -1244,7 +1246,7 @@ | |||
} | |||
.colorChange { | |||
background-color: #409effd6; | |||
background-color: #1684FC; | |||
color: #FFFF; | |||
cursor: default; | |||
} | |||
@@ -1272,7 +1274,7 @@ | |||
width: 100%; | |||
} | |||
.item_content{ | |||
color: #409eff; | |||
color:#0366D6; | |||
margin-top: 10px; | |||
font-weight:bold; | |||
} | |||
@@ -49,15 +49,18 @@ | |||
<el-table-column | |||
label="ID" | |||
prop="ID" | |||
align="center" | |||
align="left" | |||
stripe | |||
> | |||
</el-table-column> | |||
<el-table-column | |||
label="用户名" | |||
align="center" | |||
align="left" | |||
prop="Name" | |||
width="100px"> | |||
<template slot-scope="scope"> | |||
<a :href="AppSubUrl +'../../../'+ scope.row.Name">{{scope.row.Name}} </a> | |||
</template> | |||
</el-table-column> | |||
<el-table-column | |||
prop="CodeMergeCount" | |||
@@ -475,10 +478,10 @@ | |||
.btnFirst{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:4px 0px 0px 4px; | |||
@@ -486,20 +489,20 @@ | |||
.btn{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
border-right: none; | |||
background: #FFFF; | |||
color: #409eff; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
} | |||
.btnLast{ | |||
line-height: 1.5; | |||
margin: -3.5px; | |||
border: 1px solid #409eff; | |||
border: 1px solid rgba(22, 132, 252, 100); | |||
/* border-right: none; */ | |||
background: #FFFF; | |||
color: #409eff; | |||
color: #1684FC; | |||
width: 60px; | |||
height: 30px; | |||
border-radius:0px 4px 4px 0px; | |||
@@ -528,7 +531,7 @@ | |||
} | |||
.colorChange { | |||
background-color: #409effd6; | |||
background-color: #1684FC; | |||
color: #FFFF; | |||
cursor: default; | |||
} | |||