| @@ -139,6 +139,7 @@ func init() { | |||
| new(RepoStatistic), | |||
| new(SummaryStatistic), | |||
| new(UserBusinessAnalysis), | |||
| new(UserLoginLog), | |||
| ) | |||
| gonicNames := []string{"SSL", "UID"} | |||
| @@ -1,13 +1,115 @@ | |||
| package models | |||
| import "code.gitea.io/gitea/modules/git" | |||
| import ( | |||
| "fmt" | |||
| "strings" | |||
| "time" | |||
| "code.gitea.io/gitea/modules/git" | |||
| ) | |||
| func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | |||
| wikiPath := "" | |||
| if repo.HasWiki() { | |||
| wikiPath = repo.WikiPath() | |||
| } | |||
| return git.GetRepoKPIStats(repo.RepoPath(), wikiPath) | |||
| return getRepoKPIStats(repo.RepoPath(), wikiPath) | |||
| } | |||
| func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error) { | |||
| stats := &git.RepoKPIStats{} | |||
| contributors, err := git.GetContributors(repoPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| timeUntil := time.Now() | |||
| fourMonthAgo := timeUntil.AddDate(0, -4, 0) | |||
| recentlyContributors, err := git.GetContributorsDetail(repoPath, fourMonthAgo) | |||
| newContributersDict := make(map[string]struct{}) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if contributors != nil { | |||
| contributorDistinctDict := make(map[string]int, 0) | |||
| keyContributorsDict := make(map[string]struct{}, 0) | |||
| for _, contributor := range contributors { | |||
| if strings.Compare(contributor.Email, "") == 0 { | |||
| continue | |||
| } | |||
| user, err := GetUserByActivateEmail(contributor.Email) | |||
| if err == nil { | |||
| value, ok := contributorDistinctDict[user.Email] | |||
| if !ok { | |||
| contributorDistinctDict[user.Email] = contributor.CommitCnt | |||
| } else { | |||
| contributorDistinctDict[user.Email] = value + contributor.CommitCnt | |||
| } | |||
| setKeyContributerDict(contributorDistinctDict, user.Email, keyContributorsDict) | |||
| } else { | |||
| value, ok := contributorDistinctDict[contributor.Email] | |||
| if !ok { | |||
| contributorDistinctDict[contributor.Email] = contributor.CommitCnt | |||
| } else { | |||
| contributorDistinctDict[contributor.Email] = value + contributor.CommitCnt | |||
| } | |||
| setKeyContributerDict(contributorDistinctDict, contributor.Email, keyContributorsDict) | |||
| } | |||
| } | |||
| if recentlyContributors != nil { | |||
| for _, recentlyContributor := range recentlyContributors { | |||
| user, err := GetUserByActivateEmail(recentlyContributor.Email) | |||
| var ok bool | |||
| if err == nil { | |||
| _, ok = contributorDistinctDict[user.Email] | |||
| } else { | |||
| _, ok = contributorDistinctDict[recentlyContributor.Email] | |||
| } | |||
| if !ok { | |||
| stats.ContributorsAdded++ | |||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||
| } | |||
| } | |||
| } | |||
| stats.Contributors = int64(len(contributorDistinctDict)) | |||
| stats.KeyContributors = int64(len(keyContributorsDict)) | |||
| } | |||
| err = git.SetDevelopAge(repoPath, stats) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||
| } | |||
| err = git.SetRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||
| } | |||
| git.SetWikiPages(wikiPath, stats) | |||
| return stats, nil | |||
| } | |||
| func setKeyContributerDict(contributorDistinctDict map[string]int, email string, keyContributorsDict map[string]struct{}) { | |||
| if contributorDistinctDict[email] >= 3 { | |||
| _, ok := keyContributorsDict[email] | |||
| if !ok { | |||
| keyContributorsDict[email] = struct{}{} | |||
| } | |||
| } | |||
| } | |||
| func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { | |||
| @@ -114,7 +114,7 @@ func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis | |||
| return userBusinessAnalysisReturnList | |||
| } | |||
| func CountData(wikiCountMap map[string]int) { | |||
| func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { | |||
| log.Info("start to count other user info data") | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| @@ -125,12 +125,12 @@ func CountData(wikiCountMap map[string]int) { | |||
| currentTimeNow := time.Now() | |||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | |||
| yesterday := currentTimeNow.AddDate(0, 0, -1) | |||
| startTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location()) | |||
| //yesterday := currentTimeNow.AddDate(0, 0, -1) | |||
| //startTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location()) | |||
| start_unix := startTime.Unix() | |||
| log.Info("DB query time:" + startTime.Format("2006-01-02 15:04:05")) | |||
| endTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | |||
| //endTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | |||
| end_unix := endTime.Unix() | |||
| CountDate := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 1, 0, 0, currentTimeNow.Location()) | |||
| @@ -153,12 +153,19 @@ func CountData(wikiCountMap map[string]int) { | |||
| CommitDatasetSizeMap := queryDatasetSize(start_unix, end_unix) | |||
| SolveIssueCountMap := querySolveIssue(start_unix, end_unix) | |||
| CreateRepoCountMap := queryUserCreateRepo(start_unix, end_unix) | |||
| LoginCountMap := queryLoginCount(start_unix, end_unix) | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| for i, userRecord := range userList { | |||
| var dateRecord UserBusinessAnalysis | |||
| dateRecord.ID = userRecord.ID | |||
| log.Info("i=" + fmt.Sprint(i) + " userName=" + userRecord.Name) | |||
| dateRecord.CountDate = CountDate.Unix() | |||
| statictisSess.Delete(&dateRecord) | |||
| dateRecord.Email = userRecord.Email | |||
| dateRecord.RegistDate = userRecord.CreatedUnix | |||
| dateRecord.Name = userRecord.Name | |||
| @@ -235,10 +242,14 @@ func CountData(wikiCountMap map[string]int) { | |||
| dateRecord.CreateRepoCount = CreateRepoCountMap[dateRecord.ID] | |||
| } | |||
| if _, ok := LoginCountMap[dateRecord.ID]; !ok { | |||
| dateRecord.LoginCount = 0 | |||
| } else { | |||
| dateRecord.LoginCount = LoginCountMap[dateRecord.ID] | |||
| } | |||
| dateRecord.CommitModelCount = 0 | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| statictisSess.Insert(&dateRecord) | |||
| } | |||
| @@ -421,6 +432,24 @@ func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | |||
| return resultMap | |||
| } | |||
| func queryLoginCount(start_unix int64, end_unix int64) map[int64]int { | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| statictisSess.Select("id,u_id").Table("user_login_log").Where("created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)) | |||
| userLoginLogList := make([]*UserLoginLog, 0) | |||
| statictisSess.Find(&userLoginLogList) | |||
| resultMap := make(map[int64]int) | |||
| log.Info("query user login size=" + fmt.Sprint(len(userLoginLogList))) | |||
| for _, loginRecord := range userLoginLogList { | |||
| if _, ok := resultMap[loginRecord.UId]; !ok { | |||
| resultMap[loginRecord.UId] = 1 | |||
| } else { | |||
| resultMap[loginRecord.UId] += 1 | |||
| } | |||
| } | |||
| return resultMap | |||
| } | |||
| func subMonth(t1, t2 time.Time) (month int) { | |||
| y1 := t1.Year() | |||
| y2 := t2.Year() | |||
| @@ -0,0 +1,34 @@ | |||
| package models | |||
| import ( | |||
| "net/http" | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| ) | |||
| type UserLoginLog struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| UId int64 `xorm:"NOT NULL"` | |||
| IpAddr string `xorm:"default NULL"` | |||
| CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||
| } | |||
| func SaveLoginInfoToDb(r *http.Request, u *User) { | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| var dateRecord UserLoginLog | |||
| dateRecord.UId = u.ID | |||
| dateRecord.IpAddr = getIP(r) | |||
| statictisSess.Insert(&dateRecord) | |||
| } | |||
| func getIP(r *http.Request) string { | |||
| forwarded := r.Header.Get("X-FORWARDED-FOR") | |||
| if forwarded != "" { | |||
| return forwarded | |||
| } | |||
| return r.RemoteAddr | |||
| } | |||
| @@ -35,58 +35,7 @@ type UserKPITypeStats struct { | |||
| isNewContributor bool //是否是4个月内的新增贡献者 | |||
| } | |||
| func GetRepoKPIStats(repoPath string, wikiPath string) (*RepoKPIStats, error) { | |||
| stats := &RepoKPIStats{} | |||
| contributors, err := GetContributors(repoPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| timeUntil := time.Now() | |||
| fourMonthAgo := timeUntil.AddDate(0, -4, 0) | |||
| recentlyContributors, err := getContributors(repoPath, fourMonthAgo) | |||
| newContributersDict := make(map[string]struct{}) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if contributors != nil { | |||
| stats.Contributors = int64(len(contributors)) | |||
| for _, contributor := range contributors { | |||
| if contributor.CommitCnt >= 3 { | |||
| stats.KeyContributors++ | |||
| } | |||
| if recentlyContributors != nil { | |||
| for _, recentlyContributor := range recentlyContributors { | |||
| if recentlyContributor.Email == contributor.Email && recentlyContributor.CommitCnt == contributor.CommitCnt { | |||
| stats.ContributorsAdded++ | |||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| err = setDevelopAge(repoPath, stats) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||
| } | |||
| err = setRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||
| } | |||
| setWikiPages(wikiPath, stats) | |||
| return stats, nil | |||
| } | |||
| func setDevelopAge(repoPath string, stats *RepoKPIStats) error { | |||
| func SetDevelopAge(repoPath string, stats *RepoKPIStats) error { | |||
| args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short"} | |||
| stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | |||
| if err != nil { | |||
| @@ -173,7 +122,7 @@ func GetUserKPIStats(repoPath string) (map[string]*UserKPIStats, error) { | |||
| } | |||
| func setRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, newContributers map[string]struct{}) error { | |||
| func SetRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, newContributers map[string]struct{}) error { | |||
| since := fromTime.Format(time.RFC3339) | |||
| args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} | |||
| @@ -259,7 +208,7 @@ func setRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, n | |||
| } | |||
| func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error) { | |||
| func GetContributorsDetail(repoPath string, fromTime time.Time) ([]Contributor, error) { | |||
| since := fromTime.Format(time.RFC3339) | |||
| cmd := NewCommand("shortlog", "-sne", "--all", fmt.Sprintf("--since='%s'", since)) | |||
| stdout, err := cmd.RunInDir(repoPath) | |||
| @@ -289,7 +238,7 @@ func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error) | |||
| return nil, nil | |||
| } | |||
| func setWikiPages(wikiPath string, stats *RepoKPIStats) { | |||
| func SetWikiPages(wikiPath string, stats *RepoKPIStats) { | |||
| wikiPages := 0 | |||
| if wikiPath == "" { | |||
| @@ -34,7 +34,7 @@ func GetImpactInitValue(watch int64, star int64, fork int64, download int64, com | |||
| setting.RadarMap.ImpactFork*float64(fork) + | |||
| setting.RadarMap.ImpactCodeDownload*float64(download)*0.001 + | |||
| setting.RadarMap.ImpactComments*float64(comments) + | |||
| setting.RadarMap.ImpactBrowser*float64(browser) | |||
| setting.RadarMap.ImpactBrowser*float64(browser)*0.001 | |||
| } | |||
| @@ -41,7 +41,7 @@ function setDataSetTask(){ | |||
| //dislpayUser(); | |||
| getLabelPropertyTask(); | |||
| displayLabelPropertyTask(); | |||
| dataset_sele_Change(""); | |||
| $(".ui.dataset.modal").modal("show"); | |||
| } | |||
| @@ -132,6 +132,7 @@ function setPredictTask(){ | |||
| get_model_list(); | |||
| displayModelTask(); | |||
| dataset_auto_sele_Change(""); | |||
| $(".ui.predict.modal").modal("show"); | |||
| } | |||
| @@ -197,13 +198,13 @@ function sele_export_Change(sele){ | |||
| function dataset_sele_Change(sele){ | |||
| var dataset_listName = $('#dataset_list option:selected').text(); | |||
| console.log("select dataset_list =" + dataset_listName); | |||
| $("#datasetlabeltaskname").attr({value:dataset_listName+"-人工标注"}); | |||
| $("#datasetlabeltaskname").val(dataset_listName+"-人工标注"); | |||
| } | |||
| function dataset_auto_sele_Change(sele){ | |||
| var dataset_listName = $('#dataset_list_auto option:selected').text(); | |||
| console.log("select dataset_list_auto =" + dataset_listName); | |||
| $("#autolabeltaskname").attr({value:dataset_listName+"-自动标注"}); | |||
| $("#autolabeltaskname").val(dataset_listName+"-自动标注"); | |||
| } | |||
| @@ -41,4 +41,6 @@ func UpdateAllRepoCommitCnt(ctx *macaron.Context) { | |||
| func RepoStatisticManually(ctx *macaron.Context) { | |||
| date := ctx.Query("date") | |||
| repo.RepoStatisticDaily(date) | |||
| repo.SummaryStatisticDaily(date) | |||
| repo.TimingCountDataByDate(date) | |||
| } | |||
| @@ -278,7 +278,21 @@ func GetAttachment(ctx *context.Context) { | |||
| ctx.ServerError("Update", err) | |||
| return | |||
| } | |||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||
| if dataSet != nil { | |||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||
| } else { | |||
| fr, err := storage.Attachments.Open(attach.RelativePath()) | |||
| if err != nil { | |||
| ctx.ServerError("Open", err) | |||
| return | |||
| } | |||
| defer fr.Close() | |||
| if err = ServeData(ctx, attach.Name, fr); err != nil { | |||
| ctx.ServerError("ServeData", err) | |||
| return | |||
| } | |||
| } | |||
| } else { | |||
| fr, err := storage.Attachments.Open(attach.RelativePath()) | |||
| if err != nil { | |||
| @@ -8,16 +8,17 @@ import ( | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| func TimingCountData() { | |||
| func TimingCountDataByDate(date string) { | |||
| t, _ := time.Parse("2006-01-02", date) | |||
| startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | |||
| endTime := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location()) | |||
| //query wiki data | |||
| log.Info("start to time count data") | |||
| wikiMap := make(map[string]int) | |||
| currentTimeNow := time.Now() | |||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | |||
| yesterday := currentTimeNow.AddDate(0, 0, -1) | |||
| repoList, err := models.GetAllRepositories() | |||
| if err != nil { | |||
| log.Error("query repo error.") | |||
| @@ -29,7 +30,7 @@ func TimingCountData() { | |||
| time, err := git.GetLatestCommitTime(wikiPath) | |||
| if err == nil { | |||
| log.Info("last commit time:" + time.Format("2006-01-02 15:04:05") + " wikiPath=" + wikiPath) | |||
| if time.After(yesterday) { | |||
| if time.After(startTime) { | |||
| wikiRepo, _, err := FindWikiRepoCommitByWikiPath(wikiPath) | |||
| if err != nil { | |||
| log.Error("wiki not exist. wikiPath=" + wikiPath) | |||
| @@ -55,5 +56,16 @@ func TimingCountData() { | |||
| } | |||
| } | |||
| //other user info data | |||
| models.CountData(wikiMap) | |||
| models.CounDataByDate(wikiMap, startTime, endTime) | |||
| } | |||
| func TimingCountData() { | |||
| log.Info("start to time count data") | |||
| currentTimeNow := time.Now() | |||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | |||
| startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") | |||
| TimingCountDataByDate(startTime) | |||
| } | |||
| @@ -215,6 +215,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||
| } | |||
| return | |||
| } | |||
| models.SaveLoginInfoToDb(ctx.Req.Request, u) | |||
| // If this user is enrolled in 2FA, we can't sign the user in just yet. | |||
| // Instead, redirect them to the 2FA authentication page. | |||
| _, err = models.GetTwoFactorByUID(u.ID) | |||
| @@ -226,7 +227,6 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||
| } | |||
| return | |||
| } | |||
| // User needs to use 2FA, save data and redirect to 2FA page. | |||
| if err := ctx.Session.Set("twofaUid", u.ID); err != nil { | |||
| ctx.ServerError("UserSignIn: Unable to set twofaUid in session", err) | |||
| @@ -240,7 +240,6 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||
| ctx.ServerError("UserSignIn: Unable to save session", err) | |||
| return | |||
| } | |||
| regs, err := models.GetU2FRegistrationsByUID(u.ID) | |||
| if err == nil && len(regs) > 0 { | |||
| ctx.Redirect(setting.AppSubURL + "/user/u2f") | |||
| @@ -1168,8 +1167,8 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo | |||
| log.Trace("Account created: %s", u.Name, ctx.Data["MsgID"]) | |||
| err := models.AddEmailAddress(&models.EmailAddress{ | |||
| UID: u.ID, | |||
| Email: form.Email, | |||
| UID: u.ID, | |||
| Email: form.Email, | |||
| IsActivated: !setting.Service.RegisterEmailConfirm, | |||
| }) | |||
| @@ -1267,7 +1266,7 @@ func Activate(ctx *context.Context) { | |||
| } | |||
| email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email) | |||
| if err != nil || email == nil{ | |||
| if err != nil || email == nil { | |||
| log.Error("GetEmailAddressByIDAndEmail failed", ctx.Data["MsgID"]) | |||
| } else { | |||
| if err := email.Activate(); err != nil { | |||
| @@ -31,7 +31,7 @@ | |||
| <div class="description">Contains build settings for gulp</div> | |||
| </div> | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -233,6 +233,8 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;} | |||
| .icon-bind{background-position: -550px -52px;} | |||
| .icon-unbind{background-position: -568px -52px;} | |||
| .CREATING, .STOPPING, .DELETING, .STARTING, .WAITING{display:inline-block;background-image:url('/img/loading.gif');background-repeat:no-repeat;width:16px;height:16px;background-size:16px 16px;margin-right:5px;} | |||
| .COMPLETED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -441px -52px;} | |||
| .text_over{ | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||