Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/987tags/v1.21.12.1
| @@ -17,6 +17,7 @@ require ( | |||
| gitea.com/macaron/macaron v1.4.0 | |||
| gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | |||
| gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 | |||
| github.com/360EntSecGroup-Skylar/excelize/v2 v2.0.2 | |||
| github.com/BurntSushi/toml v0.3.1 | |||
| github.com/PuerkitoBio/goquery v1.5.0 | |||
| github.com/RichardKnop/machinery v1.6.9 | |||
| @@ -51,6 +51,8 @@ gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14m | |||
| gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks= | |||
| gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= | |||
| gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= | |||
| github.com/360EntSecGroup-Skylar/excelize/v2 v2.0.2 h1:StMrA6UQ5Cm6206DxXGuV/NMqSIOIDoMXMYt8JPe1lE= | |||
| github.com/360EntSecGroup-Skylar/excelize/v2 v2.0.2/go.mod h1:EfRHD2k+Kd7ijnqlwOrH1IifwgWB9yYJ0pdXtBZmlpU= | |||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | |||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | |||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | |||
| @@ -538,6 +540,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ | |||
| github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | |||
| github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= | |||
| github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | |||
| github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= | |||
| github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= | |||
| github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA= | |||
| github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= | |||
| github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= | |||
| @@ -818,6 +822,7 @@ golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPh | |||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | |||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | |||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | |||
| golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | |||
| golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | |||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | |||
| golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | |||
| @@ -52,26 +52,46 @@ type Cloudbrain struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| JobID string `xorm:"INDEX NOT NULL"` | |||
| JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"` | |||
| JobName string `xorm:"INDEX"` | |||
| Status string `xorm:"INDEX"` | |||
| UserID int64 `xorm:"INDEX"` | |||
| RepoID int64 `xorm:"INDEX"` | |||
| SubTaskName string `xorm:"INDEX"` | |||
| JobName string | |||
| Status string | |||
| UserID int64 | |||
| RepoID int64 | |||
| SubTaskName string | |||
| ContainerID string | |||
| ContainerIp string | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| Duration int64 `xorm:"INDEX duration"` | |||
| Duration int64 | |||
| TrainJobDuration string | |||
| DeletedAt time.Time `xorm:"deleted"` | |||
| CanDebug bool `xorm:"-"` | |||
| CanDel bool `xorm:"-"` | |||
| Type int `xorm:"INDEX DEFAULT 0"` | |||
| VersionID int64 `xorm:"INDEX DEFAULT 0"` | |||
| VersionName string | |||
| Uuid string | |||
| DatasetName string | |||
| Type int | |||
| VersionID int64 //版本id | |||
| VersionName string `xorm:"INDEX"` //当前版本 | |||
| Uuid string //数据集id | |||
| DatasetName string | |||
| VersionCount int //任务的当前版本数量,不包括删除的 | |||
| IsLatestVersion string //是否是最新版本,1是,0否 | |||
| CommitID string //提交的仓库代码id | |||
| PreVersionName string //父版本名称 | |||
| ComputeResource string //计算资源,例如npu | |||
| EngineID int64 //引擎id | |||
| TrainUrl string //输出的obs路径 | |||
| BranchName string //分支名称 | |||
| Parameters string //传给modelarts的param参数 | |||
| BootFile string //启动文件 | |||
| DataUrl string //数据集的obs路径 | |||
| LogUrl string //日志输出的obs路径 | |||
| PreVersionId int64 //父版本的版本id | |||
| FlavorCode string //modelarts上的规格id | |||
| Description string `xorm:"varchar(256)"` //描述 | |||
| WorkServerNumber int //节点数 | |||
| FlavorName string //规格名称 | |||
| EngineName string //引擎名称 | |||
| TotalVersionCount int //任务的所有版本数量,包括删除的 | |||
| User *User `xorm:"-"` | |||
| Repo *Repository `xorm:"-"` | |||
| @@ -150,13 +170,16 @@ type CloudbrainsOptions struct { | |||
| ListOptions | |||
| RepoID int64 // include all repos if empty | |||
| UserID int64 | |||
| JobID int64 | |||
| JobID string | |||
| SortType string | |||
| CloudbrainIDs []int64 | |||
| // JobStatus CloudbrainStatus | |||
| Type int | |||
| JobType string | |||
| Type int | |||
| JobType string | |||
| VersionName string | |||
| IsLatestVersion string | |||
| } | |||
| type TaskPod struct { | |||
| TaskRoleStatus struct { | |||
| Name string `json:"name"` | |||
| @@ -353,6 +376,7 @@ type FlavorInfos struct { | |||
| type FlavorInfo struct { | |||
| Id int `json:"id"` | |||
| Value string `json:"value"` | |||
| Desc string `json:"desc"` | |||
| } | |||
| type PoolInfos struct { | |||
| @@ -578,20 +602,33 @@ type Config struct { | |||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||
| Parameter []Parameter `json:"parameter"` | |||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||
| //DatasetID string `json:"dataset_id"` | |||
| //DataVersionID string `json:"dataset_version_id"` | |||
| //DataSource []DataSource `json:"data_source"` | |||
| //SpecID int64 `json:"spec_id"` | |||
| EngineID int64 `json:"engine_id"` | |||
| //ModelID int64 `json:"model_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| EngineID int64 `json:"engine_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| //UserImageUrl string `json:"user_image_url"` | |||
| //UserCommand string `json:"user_command"` | |||
| CreateVersion bool `json:"create_version"` | |||
| //Volumes []Volumes `json:"volumes"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| CreateVersion bool `json:"create_version"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| } | |||
| type CreateTrainJobVersionParams struct { | |||
| Description string `json:"job_desc"` | |||
| Config TrainJobVersionConfig `json:"config"` | |||
| } | |||
| type TrainJobVersionConfig struct { | |||
| WorkServerNum int `json:"worker_server_num"` | |||
| AppUrl string `json:"app_url"` //训练作业的代码目录 | |||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||
| Parameter []Parameter `json:"parameter"` | |||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||
| EngineID int64 `json:"engine_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| PreVersionId int64 `json:"pre_version_id"` | |||
| } | |||
| type CreateConfigParams struct { | |||
| @@ -602,20 +639,11 @@ type CreateConfigParams struct { | |||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||
| Parameter []Parameter `json:"parameter"` | |||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||
| //DatasetID string `json:"dataset_id"` | |||
| //DataVersionID string `json:"dataset_version_id"` | |||
| //DataSource []DataSource `json:"data_source"` | |||
| //SpecID int64 `json:"spec_id"` | |||
| EngineID int64 `json:"engine_id"` | |||
| //ModelID int64 `json:"model_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| //UserImageUrl string `json:"user_image_url"` | |||
| //UserCommand string `json:"user_command"` | |||
| //CreateVersion bool `json:"create_version"` | |||
| //Volumes []Volumes `json:"volumes"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| EngineID int64 `json:"engine_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| } | |||
| type Parameter struct { | |||
| @@ -729,18 +757,10 @@ type GetConfigResult struct { | |||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||
| Parameter []Parameter `json:"parameter"` | |||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||
| //DatasetID string `json:"dataset_id"` | |||
| //DataVersionID string `json:"dataset_version_id"` | |||
| //DataSource []DataSource `json:"data_source"` | |||
| //SpecID int64 `json:"spec_id"` | |||
| EngineID int64 `json:"engine_id"` | |||
| //ModelID int64 `json:"model_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| //UserImageUrl string `json:"user_image_url"` | |||
| //UserCommand string `json:"user_command"` | |||
| //CreateVersion bool `json:"create_version"` | |||
| //Volumes []Volumes `json:"volumes"` | |||
| EngineID int64 `json:"engine_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| } | |||
| @@ -771,25 +791,18 @@ type GetTrainJobResult struct { | |||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||
| Parameter []Parameter `json:"parameter"` | |||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||
| //DatasetID string `json:"dataset_id"` | |||
| //DataVersionID string `json:"dataset_version_id"` | |||
| //DataSource []DataSource `json:"data_source"` | |||
| //SpecID int64 `json:"spec_id"` | |||
| EngineID int64 `json:"engine_id"` | |||
| EngineName string `json:"engine_name"` | |||
| EngineVersion string `json:"engine_version"` | |||
| //ModelID int64 `json:"model_id"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| //UserImageUrl string `json:"user_image_url"` | |||
| //UserCommand string `json:"user_command"` | |||
| //Volumes []Volumes `json:"volumes"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| PoolName string `json:"pool_name"` | |||
| NasMountPath string `json:"nas_mount_path"` | |||
| NasShareAddr string `json:"nas_share_addr"` | |||
| DatasetName string | |||
| EngineID int64 `json:"engine_id"` | |||
| EngineName string `json:"engine_name"` | |||
| EngineVersion string `json:"engine_version"` | |||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||
| LogUrl string `json:"log_url"` | |||
| Flavor Flavor `json:"flavor"` | |||
| PoolID string `json:"pool_id"` | |||
| PoolName string `json:"pool_name"` | |||
| NasMountPath string `json:"nas_mount_path"` | |||
| NasShareAddr string `json:"nas_share_addr"` | |||
| DatasetName string | |||
| ModelMetricList string `json:"model_metric_list"` //列表里包含f1_score,recall,precision,accuracy,若有的话 | |||
| } | |||
| type GetTrainJobLogResult struct { | |||
| @@ -836,7 +849,7 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
| ) | |||
| } | |||
| if (opts.JobID) > 0 { | |||
| if (opts.JobID) != "" { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.job_id": opts.JobID}, | |||
| ) | |||
| @@ -854,16 +867,11 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
| ) | |||
| } | |||
| // switch opts.JobStatus { | |||
| // case JobWaiting: | |||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)}) | |||
| // case JobFailed: | |||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobFailed)}) | |||
| // case JobStopped: | |||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobStopped)}) | |||
| // case JobSucceeded: | |||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobSucceeded)}) | |||
| // } | |||
| if (opts.IsLatestVersion) != "" { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.is_latest_version": opts.IsLatestVersion}, | |||
| ) | |||
| } | |||
| if len(opts.CloudbrainIDs) > 0 { | |||
| cond = cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | |||
| @@ -891,16 +899,79 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
| Find(&cloudbrains); err != nil { | |||
| return nil, 0, fmt.Errorf("Find: %v", err) | |||
| } | |||
| sess.Close() | |||
| return cloudbrains, count, nil | |||
| } | |||
| func CloudbrainsVersionList(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int, error) { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| var cond = builder.NewCond() | |||
| if opts.RepoID > 0 { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.repo_id": opts.RepoID}, | |||
| ) | |||
| } | |||
| if opts.UserID > 0 { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.user_id": opts.UserID}, | |||
| ) | |||
| } | |||
| if (opts.Type) >= 0 { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.type": opts.Type}, | |||
| ) | |||
| } | |||
| if (opts.JobID) != "" { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.job_id": opts.JobID}, | |||
| ) | |||
| } | |||
| if (opts.JobType) != "" { | |||
| cond = cond.And( | |||
| builder.Eq{"cloudbrain.job_type": opts.JobType}, | |||
| ) | |||
| } | |||
| if len(opts.CloudbrainIDs) > 0 { | |||
| cond = cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | |||
| } | |||
| count, err := sess.Where(cond).Count(new(Cloudbrain)) | |||
| if err != nil { | |||
| return nil, 0, fmt.Errorf("Count: %v", err) | |||
| } | |||
| if opts.Page >= 0 && opts.PageSize > 0 { | |||
| var start int | |||
| if opts.Page == 0 { | |||
| start = 0 | |||
| } else { | |||
| start = (opts.Page - 1) * opts.PageSize | |||
| } | |||
| sess.Limit(opts.PageSize, start) | |||
| } | |||
| sess.OrderBy("cloudbrain.created_unix DESC") | |||
| cloudbrains := make([]*CloudbrainInfo, 0, setting.UI.IssuePagingNum) | |||
| if err := sess.Table(&Cloudbrain{}).Where(cond). | |||
| Join("left", "`user`", "cloudbrain.user_id = `user`.id"). | |||
| Find(&cloudbrains); err != nil { | |||
| return nil, 0, fmt.Errorf("Find: %v", err) | |||
| } | |||
| return cloudbrains, int(count), nil | |||
| } | |||
| func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { | |||
| if _, err = x.Insert(cloudbrain); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| @@ -924,6 +995,16 @@ func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { | |||
| return getRepoCloudBrain(cb) | |||
| } | |||
| func GetCloudbrainByJobIDAndVersionName(jobID string, versionName string) (*Cloudbrain, error) { | |||
| cb := &Cloudbrain{JobID: jobID, VersionName: versionName} | |||
| return getRepoCloudBrain(cb) | |||
| } | |||
| func GetCloudbrainByJobIDAndIsLatestVersion(jobID string, isLatestVersion string) (*Cloudbrain, error) { | |||
| cb := &Cloudbrain{JobID: jobID, IsLatestVersion: isLatestVersion} | |||
| return getRepoCloudBrain(cb) | |||
| } | |||
| func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) { | |||
| cloudBrains := make([]*Cloudbrain, 0) | |||
| err := x.Cols("job_id", "status", "type").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains) | |||
| @@ -948,6 +1029,12 @@ func SetTrainJobStatusByJobID(jobID string, status string, duration int64, train | |||
| return | |||
| } | |||
| func SetVersionCountAndLatestVersion(jobID string, versionName string, versionCount int, isLatestVersion string, totalVersionCount int) (err error) { | |||
| cb := &Cloudbrain{JobID: jobID, VersionName: versionName, VersionCount: versionCount, IsLatestVersion: isLatestVersion, TotalVersionCount: totalVersionCount} | |||
| _, err = x.Cols("version_Count", "is_latest_version", "total_version_count").Where("cloudbrain.job_id=? AND cloudbrain.version_name=?", jobID, versionName).Update(cb) | |||
| return | |||
| } | |||
| func UpdateJob(job *Cloudbrain) error { | |||
| return updateJob(x, job) | |||
| } | |||
| @@ -959,16 +1046,16 @@ func updateJob(e Engine, job *Cloudbrain) error { | |||
| return err | |||
| } | |||
| // func UpdateTrainJob(job *CloudbrainInfo) error { | |||
| // return updateTrainJob(x, job) | |||
| // } | |||
| func UpdateTrainJobVersion(job *Cloudbrain) error { | |||
| return updateJobTrainVersion(x, job) | |||
| } | |||
| // func updateTrainJob(e Engine, job *CloudbrainInfo) error { | |||
| // var sess *xorm.Session | |||
| // sess = e.Where("job_id = ?", job.Cloudbrain.JobID) | |||
| // _, err := sess.Cols("status", "container_id", "container_ip").Update(job) | |||
| // return err | |||
| // } | |||
| func updateJobTrainVersion(e Engine, job *Cloudbrain) error { | |||
| var sess *xorm.Session | |||
| sess = e.Where("job_id = ? AND version_name=?", job.JobID, job.VersionName) | |||
| _, err := sess.Cols("status", "train_job_duration").Update(job) | |||
| return err | |||
| } | |||
| func DeleteJob(job *Cloudbrain) error { | |||
| return deleteJob(x, job) | |||
| @@ -1,6 +1,8 @@ | |||
| package models | |||
| import ( | |||
| "fmt" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "xorm.io/xorm" | |||
| ) | |||
| @@ -10,10 +12,20 @@ type CustomMigration struct { | |||
| Migrate func(*xorm.Engine) error | |||
| } | |||
| type CustomMigrationStatic struct { | |||
| Description string | |||
| Migrate func(*xorm.Engine, *xorm.Engine) error | |||
| } | |||
| var customMigrations = []CustomMigration{ | |||
| {"Custom v1 Topic struct change to support chinese", syncTopicStruct}, | |||
| } | |||
| var customMigrationsStatic = []CustomMigrationStatic{ | |||
| {"Delete organization user history data ", deleteNotDisplayUser}, | |||
| {"update issue_fixed_rate to 1 if num_issues is 0 ", updateIssueFixedRate}, | |||
| } | |||
| func MigrateCustom(x *xorm.Engine) { | |||
| for _, m := range customMigrations { | |||
| @@ -27,6 +39,17 @@ func MigrateCustom(x *xorm.Engine) { | |||
| } | |||
| func MigrateCustomStatic(x *xorm.Engine, static *xorm.Engine) { | |||
| for _, m := range customMigrationsStatic { | |||
| log.Info("Migration: %s", m.Description) | |||
| if err := m.Migrate(x, static); err != nil { | |||
| log.Error("Migration: %v", err) | |||
| } | |||
| } | |||
| } | |||
| func syncTopicStruct(x *xorm.Engine) error { | |||
| query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);" | |||
| @@ -34,3 +57,27 @@ func syncTopicStruct(x *xorm.Engine) error { | |||
| _, err := x.Exec(query) | |||
| return err | |||
| } | |||
| func deleteNotDisplayUser(x *xorm.Engine, static *xorm.Engine) error { | |||
| querySQL := "select id,name from public.user where type=1" | |||
| rows, err := x.Query(querySQL) | |||
| if err != nil { | |||
| log.Info("select db failed,err:", err) | |||
| return err | |||
| } | |||
| for i, userRow := range rows { | |||
| log.Info("delete zuzi user, i=" + fmt.Sprint(i) + " userName=" + string(userRow["name"])) | |||
| deleteSql := "delete from user_business_analysis where id=" + string(userRow["id"]) + " and name='" + string(userRow["name"]) + "'" | |||
| static.Exec(deleteSql) | |||
| } | |||
| return nil | |||
| } | |||
| func updateIssueFixedRate(x *xorm.Engine, static *xorm.Engine) error { | |||
| updateSQL := "update repo_statistic set issue_fixed_rate=1.0 where num_issues=0" | |||
| _, err := static.Exec(updateSQL) | |||
| return err | |||
| } | |||
| @@ -1356,6 +1356,15 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | |||
| return accum, nil | |||
| } | |||
| func GetPullCountByUserAndRepoId(repoId int64, userId int64) int64 { | |||
| issue := new(Issue) | |||
| total, err := x.Where("is_pull=true and repo_id=? and poster_id=?", repoId, userId).Count(issue) | |||
| if err != nil { | |||
| return 0 | |||
| } | |||
| return total | |||
| } | |||
| func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) { | |||
| stats := &IssueStats{} | |||
| @@ -139,6 +139,7 @@ func init() { | |||
| new(RepoStatistic), | |||
| new(SummaryStatistic), | |||
| new(UserBusinessAnalysis), | |||
| new(UserBusinessAnalysisAll), | |||
| new(UserLoginLog), | |||
| ) | |||
| @@ -190,7 +191,7 @@ func setEngine(engine *xorm.Engine, table []interface{}, database *setting.DBInf | |||
| engine.SetMaxIdleConns(setting.Database.MaxIdleConns) | |||
| engine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | |||
| engine.Sync2(table...) | |||
| MigrateCustom(engine) | |||
| return nil | |||
| } | |||
| @@ -222,7 +223,7 @@ func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err e | |||
| if err = newEngine(ctx, migrateFunc, x, tables, setting.Database); err != nil { | |||
| return fmt.Errorf("newEngine failed: %v", err) | |||
| } | |||
| MigrateCustom(x) | |||
| xStatistic, err = getEngine(setting.DatabaseStatistic) | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to connect to database: %v", err) | |||
| @@ -230,6 +231,7 @@ func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err e | |||
| if err = newEngine(ctx, migrateFunc, xStatistic, tablesStatistic, setting.DatabaseStatistic); err != nil { | |||
| return fmt.Errorf("newEngine statistic failed: %v", err) | |||
| } | |||
| MigrateCustomStatic(x, xStatistic) | |||
| HasEngine = true | |||
| @@ -182,6 +182,7 @@ func CreateOrganization(org, owner *User) (err error) { | |||
| if _, err = sess.Insert(&OrgUser{ | |||
| UID: owner.ID, | |||
| OrgID: org.ID, | |||
| IsPublic: setting.Service.DefaultOrgMemberVisible, | |||
| }); err != nil { | |||
| return fmt.Errorf("insert org-user relation: %v", err) | |||
| } | |||
| @@ -210,9 +210,12 @@ type Repository struct { | |||
| Balance string `xorm:"NOT NULL DEFAULT '0'"` | |||
| BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"` | |||
| // git clone total count | |||
| // git clone and git pull total count | |||
| CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| // only git clone total count | |||
| GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| @@ -2473,6 +2476,24 @@ func (repo *Repository) IncreaseCloneCnt() { | |||
| return | |||
| } | |||
| func (repo *Repository) IncreaseGitCloneCnt() { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| if err := sess.Begin(); err != nil { | |||
| return | |||
| } | |||
| if _, err := sess.Exec("UPDATE `repository` SET git_clone_cnt = git_clone_cnt + 1 WHERE id = ?", repo.ID); err != nil { | |||
| return | |||
| } | |||
| if err := sess.Commit(); err != nil { | |||
| return | |||
| } | |||
| return | |||
| } | |||
| func UpdateRepositoryCommitNum(repo *Repository) error { | |||
| if _, err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil { | |||
| return err | |||
| @@ -2,29 +2,43 @@ package models | |||
| import ( | |||
| "fmt" | |||
| "sort" | |||
| "strings" | |||
| "time" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/git" | |||
| ) | |||
| type ContributorWithUserId struct { | |||
| git.Contributor | |||
| UserId int64 | |||
| IsAdmin bool | |||
| RelAvatarLink string | |||
| } | |||
| func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | |||
| wikiPath := "" | |||
| 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 { | |||
| @@ -34,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 | |||
| @@ -60,33 +74,52 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||
| setKeyContributerDict(contributorDistinctDict, contributor.Email, keyContributorsDict) | |||
| } | |||
| commitsCount += int64(contributor.CommitCnt) | |||
| } | |||
| if recentlyContributors != nil { | |||
| resentlyContributorDistinctDict := make(map[string]int, 0) | |||
| for _, recentlyContributor := range recentlyContributors { | |||
| user, err := GetUserByActivateEmail(recentlyContributor.Email) | |||
| var ok bool | |||
| if err == nil { | |||
| _, ok = contributorDistinctDict[user.Email] | |||
| value, ok := resentlyContributorDistinctDict[user.Email] | |||
| if !ok { | |||
| resentlyContributorDistinctDict[user.Email] = recentlyContributor.CommitCnt | |||
| } else { | |||
| resentlyContributorDistinctDict[user.Email] = value + recentlyContributor.CommitCnt | |||
| } | |||
| } else { | |||
| _, ok = contributorDistinctDict[recentlyContributor.Email] | |||
| value, ok := resentlyContributorDistinctDict[recentlyContributor.Email] | |||
| if !ok { | |||
| resentlyContributorDistinctDict[recentlyContributor.Email] = recentlyContributor.CommitCnt | |||
| } else { | |||
| resentlyContributorDistinctDict[recentlyContributor.Email] = value + recentlyContributor.CommitCnt | |||
| } | |||
| } | |||
| if !ok { | |||
| } | |||
| for k, v := range resentlyContributorDistinctDict { | |||
| count, ok := contributorDistinctDict[k] | |||
| if ok && count == v { | |||
| stats.ContributorsAdded++ | |||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||
| } | |||
| } | |||
| } | |||
| 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) | |||
| } | |||
| @@ -101,6 +134,72 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||
| } | |||
| func GetTop10Contributor(repoPath string) ([]*ContributorWithUserId, error) { | |||
| contributors, err := git.GetContributors(repoPath) | |||
| if err != nil { | |||
| return make([]*ContributorWithUserId, 0), err | |||
| } | |||
| contributorDistinctDict := make(map[string]*ContributorWithUserId, 0) | |||
| if contributors != nil { | |||
| 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] = &ContributorWithUserId{ | |||
| git.Contributor{ | |||
| contributor.CommitCnt, | |||
| user.Name, | |||
| user.Email, | |||
| }, | |||
| user.ID, | |||
| user.IsAdmin, | |||
| user.RelAvatarLink(), | |||
| } | |||
| } else { | |||
| value.CommitCnt += contributor.CommitCnt | |||
| } | |||
| } else { | |||
| value, ok := contributorDistinctDict[contributor.Email] | |||
| if !ok { | |||
| contributorDistinctDict[contributor.Email] = &ContributorWithUserId{ | |||
| contributor, | |||
| -1, | |||
| false, | |||
| "", | |||
| } | |||
| } else { | |||
| value.CommitCnt += contributor.CommitCnt | |||
| } | |||
| } | |||
| } | |||
| v := make([]*ContributorWithUserId, 0, len(contributorDistinctDict)) | |||
| for _, value := range contributorDistinctDict { | |||
| v = append(v, value) | |||
| } | |||
| sort.Slice(v, func(i, j int) bool { | |||
| return v[i].CommitCnt > v[j].CommitCnt | |||
| }) | |||
| if len(v) <= 10 { | |||
| return v, nil | |||
| } else { | |||
| return v[0:10], nil | |||
| } | |||
| } | |||
| return make([]*ContributorWithUserId, 0), nil | |||
| } | |||
| func setKeyContributerDict(contributorDistinctDict map[string]int, email string, keyContributorsDict map[string]struct{}) { | |||
| if contributorDistinctDict[email] >= 3 { | |||
| _, ok := keyContributorsDict[email] | |||
| @@ -122,7 +221,8 @@ func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { | |||
| for _, repository := range repositorys { | |||
| authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath()) | |||
| if err1 != nil { | |||
| return nil, err | |||
| log.Warn("get user kpi status err:"+repository.RepoPath(), err1.Error()) | |||
| continue | |||
| } | |||
| for key, value := range authorsOneRepo { | |||
| @@ -114,6 +114,17 @@ func (repo *Repository) isCollaborator(e Engine, userID int64) (bool, error) { | |||
| return e.Get(&Collaboration{RepoID: repo.ID, UserID: userID}) | |||
| } | |||
| func (repo *Repository) GetCollaboratorMode(userID int64) int { | |||
| collaboration := &Collaboration{RepoID: repo.ID, UserID: userID} | |||
| has, err := x.Get(&collaboration) | |||
| if err != nil || !has { | |||
| return -1 | |||
| } else { | |||
| return int(collaboration.Mode) | |||
| } | |||
| } | |||
| // IsCollaborator check if a user is a collaborator of a repository | |||
| func (repo *Repository) IsCollaborator(userID int64) (bool, error) { | |||
| return repo.isCollaborator(x, userID) | |||
| @@ -9,56 +9,58 @@ import ( | |||
| // RepoStatistic statistic info of all repository | |||
| type RepoStatistic struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| RepoID int64 `xorm:"unique(s) NOT NULL"` | |||
| Name string `xorm:"INDEX"` | |||
| IsPrivate bool | |||
| Date string `xorm:"unique(s) NOT NULL"` | |||
| NumWatches int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumWatchesAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumStars int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumStarsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumForks int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumForksAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumDownloads int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumDownloadsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumComments int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumCommentsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumVisits int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumClosedIssues int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumClosedIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumVersions int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumDevMonths int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| RepoSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| DatasetSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumModels int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumWikiViews int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumCommits int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumCommitsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumIssues int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumPulls int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumPullsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| IssueFixedRate float32 `xorm:"NOT NULL"` | |||
| NumContributor int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumContributorAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumKeyContributor int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumContributorsGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumCommitsGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumCommitLinesGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumIssuesGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| NumCommentsGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||
| Impact float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| Completeness float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| Liveness float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| ProjectHealth float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| TeamHealth float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| Growth float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| RadarTotal float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
| ID int64 `xorm:"pk autoincr" json:"-"` | |||
| RepoID int64 `xorm:"unique(s) NOT NULL" json:"repo_id"` | |||
| 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:"-"` | |||
| NumStars int64 `xorm:"NOT NULL DEFAULT 0" json:"star"` | |||
| NumStarsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumForks int64 `xorm:"NOT NULL DEFAULT 0" json:"fork"` | |||
| NumForksAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumDownloads int64 `xorm:"NOT NULL DEFAULT 0" json:"download"` | |||
| NumDownloadsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumComments int64 `xorm:"NOT NULL DEFAULT 0" json:"comment"` | |||
| NumCommentsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumVisits int64 `xorm:"NOT NULL DEFAULT 0" json:"view"` | |||
| NumClosedIssues int64 `xorm:"NOT NULL DEFAULT 0" json:"issueClosed"` | |||
| NumClosedIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumVersions int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumDevMonths int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| RepoSize int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| DatasetSize int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumModels int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumWikiViews int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumCommits int64 `xorm:"NOT NULL DEFAULT 0" json:"commit"` | |||
| NumCommitsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumIssues int64 `xorm:"NOT NULL DEFAULT 0" json:"issue"` | |||
| NumIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumPulls int64 `xorm:"NOT NULL DEFAULT 0" json:"pr"` | |||
| NumPullsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| IssueFixedRate float32 `xorm:"NOT NULL" json:"issueClosedRatio"` | |||
| NumContributor int64 `xorm:"NOT NULL DEFAULT 0" json:"contributor"` | |||
| NumContributorAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumKeyContributor int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumContributorsGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumCommitsGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumCommitLinesGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumIssuesGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| NumCommentsGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"` | |||
| Impact float64 `xorm:"NOT NULL DEFAULT 0" json:"impact"` | |||
| Completeness float64 `xorm:"NOT NULL DEFAULT 0" json:"completeness"` | |||
| Liveness float64 `xorm:"NOT NULL DEFAULT 0" json:"liveness"` | |||
| ProjectHealth float64 `xorm:"NOT NULL DEFAULT 0" json:"projectHealth"` | |||
| TeamHealth float64 `xorm:"NOT NULL DEFAULT 0" json:"teamHealth"` | |||
| Growth float64 `xorm:"NOT NULL DEFAULT 0" json:"growth"` | |||
| RadarTotal float64 `xorm:"NOT NULL DEFAULT 0" json:"openi"` | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"-"` | |||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"-"` | |||
| } | |||
| func DeleteRepoStatDaily(date string) error { | |||
| @@ -81,6 +83,57 @@ func DeleteRepoStatDaily(date string) error { | |||
| return nil | |||
| } | |||
| func CountRepoStatByRawSql(sql string) (int64, error) { | |||
| return xStatistic.SQL(sql).Count() | |||
| } | |||
| func GetRepoStatisticByRawSql(sql string) []*RepoStatistic { | |||
| repoStatistics := make([]*RepoStatistic, 0) | |||
| xStatistic.SQL(sql).Find(&repoStatistics) | |||
| return repoStatistics | |||
| } | |||
| func GetRepoStatLastUpdatedTime(repoId ...string) (string, string, error) { | |||
| repoStatistic := new(RepoStatistic) | |||
| var has bool | |||
| var err error | |||
| if len(repoId) == 0 { | |||
| has, err = xStatistic.Desc("created_unix").Limit(1).Cols("created_unix", "date").Get(repoStatistic) | |||
| } else { | |||
| has, err = xStatistic.Where("repo_id=?", repoId[0]).Desc("created_unix").Limit(1).Cols("created_unix", "date").Get(repoStatistic) | |||
| } | |||
| if err != nil { | |||
| return "", "", err | |||
| } else { | |||
| if has { | |||
| return repoStatistic.CreatedUnix.Format("2006-01-02 15:04:05"), repoStatistic.Date, nil | |||
| } else { | |||
| return "", "", fmt.Errorf("Can not get the latest record.") | |||
| } | |||
| } | |||
| } | |||
| func GetRepoStatisticByDateAndRepoId(date string, repoId int64) (*RepoStatistic, error) { | |||
| repoStatistic := new(RepoStatistic) | |||
| has, err := xStatistic.Where("date=? and repo_id=?", date, repoId).Get(repoStatistic) | |||
| if err != nil { | |||
| return nil, err | |||
| } else { | |||
| if has { | |||
| return repoStatistic, nil | |||
| } else { | |||
| return nil, fmt.Errorf("The num of return records is 0.") | |||
| } | |||
| } | |||
| } | |||
| func GetRepoStatisticByDate(date string, repoId int64) ([]*RepoStatistic, error) { | |||
| repoStatistics := make([]*RepoStatistic, 0) | |||
| err := xStatistic.Where("date = ? and repo_id=?", date, repoId).Find(&repoStatistics) | |||
| @@ -107,9 +160,23 @@ func InsertRepoStat(repoStat *RepoStatistic) (int64, error) { | |||
| return xStatistic.Insert(repoStat) | |||
| } | |||
| func RestoreRepoStatFork(numForks int64, repoId int64) error { | |||
| sql := "update repo_statistic set num_forks=? where repo_id=?" | |||
| _, err := xStatistic.Exec(sql, numForks, repoId) | |||
| return err | |||
| } | |||
| func UpdateRepoStat(repoStat *RepoStatistic) error { | |||
| sql := "update repo_statistic set impact=?,completeness=?,liveness=?,project_health=?,team_health=?,growth=?,radar_total=? where repo_id=? and date=?" | |||
| _, err := xStatistic.Exec(sql, repoStat.Impact, repoStat.Completeness, repoStat.Liveness, repoStat.ProjectHealth, repoStat.TeamHealth, repoStat.Growth, repoStat.RadarTotal, repoStat.RepoID, repoStat.Date) | |||
| return err | |||
| } | |||
| func UpdateRepoStatVisits(repoStat *RepoStatistic) error { | |||
| sql := "update repo_statistic set num_visits=? where repo_id=? and date=?" | |||
| _, err := xStatistic.Exec(sql, repoStat.NumVisits, repoStat.RepoID, repoStat.Date) | |||
| return err | |||
| } | |||
| @@ -1,13 +1,83 @@ | |||
| package models | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "sort" | |||
| "strconv" | |||
| "time" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "xorm.io/builder" | |||
| "xorm.io/xorm" | |||
| ) | |||
| type UserBusinessAnalysisAll struct { | |||
| ID int64 `xorm:"pk"` | |||
| CountDate int64 `xorm:"pk"` | |||
| //action :ActionMergePullRequest // 11 | |||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //action :ActionCommitRepo // 5 | |||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //action :ActionCreateIssue // 10 | |||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //comment table current date | |||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //watch table current date | |||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //star table current date | |||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //follow table | |||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| // user table | |||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||
| // | |||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||
| //attachement table | |||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||
| //0 | |||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //issue, issueassignees | |||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //baike | |||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //user | |||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||
| //repo | |||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //login count, from elk | |||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //openi index | |||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| //user | |||
| Email string `xorm:"NOT NULL"` | |||
| //user | |||
| Name string `xorm:"NOT NULL"` | |||
| DataDate string `xorm:"NULL"` | |||
| } | |||
| type UserBusinessAnalysis struct { | |||
| ID int64 `xorm:"pk"` | |||
| @@ -19,7 +89,7 @@ type UserBusinessAnalysis struct { | |||
| //action :ActionCommitRepo // 5 | |||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //action :ActionCommentIssue // 10 | |||
| //action :ActionCreateIssue // 6 | |||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //comment table current date | |||
| @@ -62,63 +132,260 @@ type UserBusinessAnalysis struct { | |||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||
| //openi index | |||
| OpenIIndex int `xorm:"NOT NULL DEFAULT 0"` | |||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||
| //user | |||
| Email string `xorm:"NOT NULL"` | |||
| //user | |||
| Name string `xorm:"NOT NULL"` | |||
| DataDate string `xorm:"NULL"` | |||
| } | |||
| type UserBusinessAnalysisQueryOptions struct { | |||
| ListOptions | |||
| UserName string | |||
| SortType string | |||
| StartTime int64 | |||
| EndTime int64 | |||
| IsAll bool | |||
| } | |||
| type UserBusinessAnalysisList []*UserBusinessAnalysis | |||
| func (ulist UserBusinessAnalysisList) Swap(i, j int) { ulist[i], ulist[j] = ulist[j], ulist[i] } | |||
| func (ulist UserBusinessAnalysisList) Len() int { return len(ulist) } | |||
| func (ulist UserBusinessAnalysisList) Less(i, j int) bool { | |||
| return ulist[i].ID > ulist[j].ID | |||
| } | |||
| type UserBusinessAnalysisAllList []*UserBusinessAnalysisAll | |||
| func (ulist UserBusinessAnalysisAllList) Swap(i, j int) { ulist[i], ulist[j] = ulist[j], ulist[i] } | |||
| func (ulist UserBusinessAnalysisAllList) Len() int { return len(ulist) } | |||
| func (ulist UserBusinessAnalysisAllList) Less(i, j int) bool { | |||
| return ulist[i].ID > ulist[j].ID | |||
| } | |||
| func getLastCountDate() int64 { | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| statictisSess.Limit(1, 0) | |||
| userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) | |||
| if err := statictisSess.Table("user_business_analysis").OrderBy("count_date desc").Limit(1, 0). | |||
| Find(&userBusinessAnalysisList); err == nil { | |||
| for _, userRecord := range userBusinessAnalysisList { | |||
| return userRecord.CountDate - 10000 | |||
| } | |||
| } else { | |||
| log.Info("query error." + err.Error()) | |||
| } | |||
| currentTimeNow := time.Now() | |||
| pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | |||
| return pageStartTime.Unix() | |||
| } | |||
| func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis { | |||
| log.Info("query startTime =" + fmt.Sprint(startTime) + " endTime=" + fmt.Sprint(endTime)) | |||
| func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysisAll, int64) { | |||
| log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime) + " isAll=" + fmt.Sprint(opts.IsAll)) | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| statictisSess.Select("*").Table("user_business_analysis").Where(" count_date>=" + fmt.Sprint(startTime) + " and count_date<=" + fmt.Sprint(endTime)).OrderBy("count_date desc") | |||
| allCount, err := statictisSess.Count(new(UserBusinessAnalysisAll)) | |||
| if err != nil { | |||
| log.Info("query error." + err.Error()) | |||
| return nil, 0 | |||
| } | |||
| log.Info("query return total:" + fmt.Sprint(allCount)) | |||
| if allCount == 0 { | |||
| RefreshUserStaticAllTabel() | |||
| } | |||
| pageSize := 1000 | |||
| totalPage := int(allCount) / pageSize | |||
| userBusinessAnalysisReturnList := UserBusinessAnalysisAllList{} | |||
| for i := 0; i <= int(totalPage); i++ { | |||
| userBusinessAnalysisAllList := make([]*UserBusinessAnalysisAll, 0) | |||
| if err := statictisSess.Table("user_business_analysis_all").OrderBy("id desc").Limit(pageSize, i*pageSize). | |||
| Find(&userBusinessAnalysisAllList); err != nil { | |||
| return nil, 0 | |||
| } | |||
| log.Info("query " + fmt.Sprint(i+1) + " result size=" + fmt.Sprint(len(userBusinessAnalysisAllList))) | |||
| for _, userRecord := range userBusinessAnalysisAllList { | |||
| userBusinessAnalysisReturnList = append(userBusinessAnalysisReturnList, userRecord) | |||
| } | |||
| } | |||
| sort.Sort(userBusinessAnalysisReturnList) | |||
| log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||
| return userBusinessAnalysisReturnList, allCount | |||
| } | |||
| func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysis, int64) { | |||
| log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime) + " isAll=" + fmt.Sprint(opts.IsAll)) | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| currentTimeNow := time.Now() | |||
| pageStartTime := getLastCountDate() | |||
| pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()).Unix() | |||
| var cond = builder.NewCond() | |||
| if len(opts.UserName) > 0 { | |||
| cond = cond.And( | |||
| builder.Like{"name", opts.UserName}, | |||
| ) | |||
| } | |||
| cond = cond.And( | |||
| builder.Gte{"count_date": pageStartTime}, | |||
| ) | |||
| cond = cond.And( | |||
| builder.Lte{"count_date": pageEndTime}, | |||
| ) | |||
| count, err := statictisSess.Where(cond).Count(new(UserBusinessAnalysis)) | |||
| if err != nil { | |||
| log.Info("query error." + err.Error()) | |||
| return nil, 0 | |||
| } | |||
| if opts.Page >= 0 && opts.PageSize > 0 { | |||
| var start int | |||
| if opts.Page == 0 { | |||
| start = 0 | |||
| } else { | |||
| start = (opts.Page - 1) * opts.PageSize | |||
| } | |||
| statictisSess.Limit(opts.PageSize, start) | |||
| } | |||
| userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) | |||
| statictisSess.Find(&userBusinessAnalysisList) | |||
| if err := statictisSess.Table("user_business_analysis").Where(cond).OrderBy("id desc"). | |||
| Find(&userBusinessAnalysisList); err != nil { | |||
| return nil, 0 | |||
| } | |||
| resultMap := make(map[int64]*UserBusinessAnalysis) | |||
| log.Info("query result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||
| for _, userRecord := range userBusinessAnalysisList { | |||
| if _, ok := resultMap[userRecord.ID]; !ok { | |||
| resultMap[userRecord.ID] = userRecord | |||
| } else { | |||
| resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||
| resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||
| resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||
| resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||
| resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||
| resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||
| resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||
| resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||
| resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||
| resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||
| resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||
| resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||
| resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||
| resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||
| } | |||
| } | |||
| userBusinessAnalysisReturnList := make([]*UserBusinessAnalysis, len(resultMap)) | |||
| index := 0 | |||
| if len(userBusinessAnalysisList) > 0 { | |||
| var newAndCond = builder.NewCond() | |||
| var newOrCond = builder.NewCond() | |||
| for _, userRecord := range userBusinessAnalysisList { | |||
| newOrCond = newOrCond.Or( | |||
| builder.Eq{"id": userRecord.ID}, | |||
| ) | |||
| } | |||
| newAndCond = newAndCond.And( | |||
| newOrCond, | |||
| ) | |||
| if !opts.IsAll { | |||
| newAndCond = newAndCond.And( | |||
| builder.Gte{"count_date": opts.StartTime}, | |||
| ) | |||
| newAndCond = newAndCond.And( | |||
| builder.Lte{"count_date": opts.EndTime}, | |||
| ) | |||
| } | |||
| allCount, err := statictisSess.Where(newAndCond).Count(new(UserBusinessAnalysis)) | |||
| if err != nil { | |||
| log.Info("query error." + err.Error()) | |||
| return nil, 0 | |||
| } | |||
| pageSize := 1000 | |||
| totalPage := int(allCount) / pageSize | |||
| for i := 0; i <= int(totalPage); i++ { | |||
| userBusinessAnalysisList = make([]*UserBusinessAnalysis, 0) | |||
| if err := statictisSess.Table("user_business_analysis").Where(newAndCond).OrderBy("count_date desc").Limit(pageSize, i*pageSize). | |||
| Find(&userBusinessAnalysisList); err != nil { | |||
| return nil, 0 | |||
| } | |||
| log.Info("query result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||
| for _, userRecord := range userBusinessAnalysisList { | |||
| if _, ok := resultMap[userRecord.ID]; !ok { | |||
| resultMap[userRecord.ID] = userRecord | |||
| } else { | |||
| resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||
| resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||
| resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||
| resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||
| resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||
| resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||
| resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||
| resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||
| resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||
| resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||
| resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||
| resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||
| resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||
| resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||
| } | |||
| } | |||
| } | |||
| } | |||
| userBusinessAnalysisReturnList := UserBusinessAnalysisList{} | |||
| for _, v := range resultMap { | |||
| userBusinessAnalysisReturnList[index] = v | |||
| index += 1 | |||
| userBusinessAnalysisReturnList = append(userBusinessAnalysisReturnList, v) | |||
| } | |||
| sort.Sort(userBusinessAnalysisReturnList) | |||
| log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||
| return userBusinessAnalysisReturnList | |||
| return userBusinessAnalysisReturnList, count | |||
| } | |||
| func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { | |||
| func RefreshUserStaticAllTabel() { | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| log.Info("delete all data from table: user_business_analysis_all") | |||
| statictisSess.Exec("delete from user_business_analysis_all") | |||
| currentTimeNow := time.Now() | |||
| pageStartTime := getLastCountDate() | |||
| pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()).Unix() | |||
| var cond = builder.NewCond() | |||
| cond = cond.And( | |||
| builder.Gte{"count_date": pageStartTime}, | |||
| ) | |||
| cond = cond.And( | |||
| builder.Lte{"count_date": pageEndTime}, | |||
| ) | |||
| userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) | |||
| if err := statictisSess.Table("user_business_analysis").Where(cond).OrderBy("id desc"). | |||
| Find(&userBusinessAnalysisList); err != nil { | |||
| return | |||
| } | |||
| log.Info("query all data from table: user_business_analysis,len=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||
| for _, userRecord := range userBusinessAnalysisList { | |||
| log.Info("insert to UserBusinessAnalysisAll table,user id=" + fmt.Sprint(userRecord.ID)) | |||
| allData := getAllData(userRecord.ID, statictisSess) | |||
| allData.ID = userRecord.ID | |||
| allData.CountDate = 0 | |||
| allData.DataDate = userRecord.DataDate | |||
| allData.Email = userRecord.Email | |||
| allData.OpenIIndex = userRecord.OpenIIndex | |||
| allData.GiteaAgeMonth = userRecord.GiteaAgeMonth | |||
| allData.Name = userRecord.Name | |||
| allData.RegistDate = userRecord.RegistDate | |||
| _, err := statictisSess.Insert(&allData) | |||
| if err != nil { | |||
| log.Info("insert all data failed." + err.Error()) | |||
| } | |||
| } | |||
| log.Info("refresh all data finished.") | |||
| } | |||
| func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, endTime time.Time, isReCount bool) error { | |||
| log.Info("start to count other user info data") | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| sess.Select("`user`.*").Table("user") | |||
| sess.Select("`user`.*").Table("user").Where("type != 1 and is_active=true") | |||
| userList := make([]*User, 0) | |||
| sess.Find(&userList) | |||
| @@ -132,12 +399,15 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||
| //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()) | |||
| if isReCount { | |||
| CountDate = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 1, 0, 0, currentTimeNow.Location()) | |||
| } | |||
| DataDate := startTime.Format("2006-01-02") | |||
| CodeMergeCountMap := queryPullRequest(start_unix, end_unix) | |||
| CommitCountMap := queryAction(start_unix, end_unix, 5) | |||
| IssueCountMap := queryAction(start_unix, end_unix, 10) | |||
| CommitCountMap := queryCommitAction(start_unix, end_unix, 5) | |||
| IssueCountMap := queryAction(start_unix, end_unix, 6) | |||
| CommentCountMap := queryComment(start_unix, end_unix) | |||
| FocusRepoCountMap := queryWatch(start_unix, end_unix) | |||
| @@ -154,6 +424,7 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||
| SolveIssueCountMap := querySolveIssue(start_unix, end_unix) | |||
| CreateRepoCountMap := queryUserCreateRepo(start_unix, end_unix) | |||
| LoginCountMap := queryLoginCount(start_unix, end_unix) | |||
| OpenIIndexMap := queryUserRepoOpenIIndex(start_unix, end_unix) | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| @@ -170,6 +441,7 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||
| dateRecord.RegistDate = userRecord.CreatedUnix | |||
| dateRecord.Name = userRecord.Name | |||
| dateRecord.GiteaAgeMonth = subMonth(currentTimeNow, userRecord.CreatedUnix.AsTime()) | |||
| dateRecord.DataDate = DataDate | |||
| if _, ok := CodeMergeCountMap[dateRecord.ID]; !ok { | |||
| dateRecord.CodeMergeCount = 0 | |||
| } else { | |||
| @@ -248,11 +520,114 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||
| dateRecord.LoginCount = LoginCountMap[dateRecord.ID] | |||
| } | |||
| if _, ok := OpenIIndexMap[dateRecord.ID]; !ok { | |||
| dateRecord.OpenIIndex = 0 | |||
| } else { | |||
| dateRecord.OpenIIndex = OpenIIndexMap[dateRecord.ID] | |||
| } | |||
| dateRecord.CommitModelCount = 0 | |||
| statictisSess.Insert(&dateRecord) | |||
| _, err = statictisSess.Insert(&dateRecord) | |||
| if err != nil { | |||
| log.Info("insert daterecord failed." + err.Error()) | |||
| return err | |||
| } | |||
| if isExistUserInAllTable(dateRecord.ID, statictisSess) { | |||
| updateCurrentData(dateRecord.ID, statictisSess, dateRecord) | |||
| } else { | |||
| log.Info("insert to UserBusinessAnalysisAll table,user id=" + fmt.Sprint(dateRecord.ID)) | |||
| allData := getAllData(dateRecord.ID, statictisSess) | |||
| allData.ID = dateRecord.ID | |||
| allData.CountDate = 0 | |||
| allData.DataDate = dateRecord.DataDate | |||
| allData.Email = dateRecord.Email | |||
| allData.OpenIIndex = dateRecord.OpenIIndex | |||
| allData.GiteaAgeMonth = dateRecord.GiteaAgeMonth | |||
| allData.Name = dateRecord.Name | |||
| allData.RegistDate = dateRecord.RegistDate | |||
| _, err = statictisSess.Insert(&allData) | |||
| if err != nil { | |||
| log.Info("insert all data failed." + err.Error()) | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func updateCurrentData(userId int64, statictisSess *xorm.Session, currentData UserBusinessAnalysis) { | |||
| _, err := statictisSess.Update("update user_business_analysis_all set code_merge_count+=" + fmt.Sprint(currentData.CodeMergeCount) + | |||
| ",commit_count+=" + fmt.Sprint(currentData.CommitCount) + | |||
| ",issue_count+=" + fmt.Sprint(currentData.IssueCount) + | |||
| ",comment_count+=" + fmt.Sprint(currentData.CommentCount) + | |||
| ",focus_repo_count+=" + fmt.Sprint(currentData.FocusRepoCount) + | |||
| ",star_repo_count+=" + fmt.Sprint(currentData.StarRepoCount) + | |||
| ",watched_count+=" + fmt.Sprint(currentData.WatchedCount) + | |||
| ",commit_code_size+=" + fmt.Sprint(currentData.CommitCodeSize) + | |||
| ",commit_dataset_size+=" + fmt.Sprint(currentData.CommitDatasetSize) + | |||
| ",commit_model_count+=" + fmt.Sprint(currentData.CommitModelCount) + | |||
| ",solve_issue_count+=" + fmt.Sprint(currentData.SolveIssueCount) + | |||
| ",encyclopedias_count+=" + fmt.Sprint(currentData.EncyclopediasCount) + | |||
| ",create_repo_count+=" + fmt.Sprint(currentData.CreateRepoCount) + | |||
| ",login_count+=" + fmt.Sprint(currentData.LoginCount) + | |||
| " where id=" + fmt.Sprint(userId)) | |||
| if err != nil { | |||
| log.Info("update table failed." + err.Error()) | |||
| } | |||
| } | |||
| func isExistUserInAllTable(userId int64, statictisSess *xorm.Session) bool { | |||
| allCount, err := statictisSess.Where("id=" + fmt.Sprint(userId)).Count(new(UserBusinessAnalysisAll)) | |||
| if err != nil { | |||
| return false | |||
| } | |||
| return allCount > 0 | |||
| } | |||
| func getAllData(userId int64, statictisSess *xorm.Session) UserBusinessAnalysisAll { | |||
| var dateRecord UserBusinessAnalysisAll | |||
| rows, err := statictisSess.Query("select sum(code_merge_count) as code_merge_count,sum(commit_count) as commit_count,sum(issue_count) as issue_count,sum(issue_count) as issue_count,sum(comment_count) as comment_count,sum(focus_repo_count) as focus_repo_count,sum(star_repo_count) as star_repo_count,sum(watched_count) as watched_count,sum(commit_code_size) as commit_code_size,sum(commit_dataset_size) as commit_dataset_size, sum(commit_model_count) as commit_model_count,sum(solve_issue_count) as solve_issue_count,sum(encyclopedias_count) as encyclopedias_count, sum(create_repo_count) as create_repo_count,sum(login_count) as login_count from public.user_business_analysis where id=" + fmt.Sprint(userId) + " group by id") | |||
| if err == nil { | |||
| for i, row := range rows { | |||
| log.Info("query user info, i=" + fmt.Sprint(i) + " code_merge_count=" + string(row["code_merge_count"])) | |||
| dateRecord.CodeMergeCount = getInt(string(row["code_merge_count"])) | |||
| dateRecord.CommitCount = getInt(string(row["commit_count"])) | |||
| dateRecord.IssueCount = getInt(string(row["issue_count"])) | |||
| dateRecord.CommentCount = getInt(string(row["comment_count"])) | |||
| dateRecord.FocusRepoCount = getInt(string(row["focus_repo_count"])) | |||
| dateRecord.StarRepoCount = getInt(string(row["star_repo_count"])) | |||
| dateRecord.WatchedCount = getInt(string(row["watched_count"])) | |||
| dateRecord.CommitCodeSize = getInt(string(row["commit_code_size"])) | |||
| dateRecord.CommitDatasetSize = getInt(string(row["commit_dataset_size"])) | |||
| dateRecord.CommitModelCount = getInt(string(row["commit_model_count"])) | |||
| dateRecord.SolveIssueCount = getInt(string(row["solve_issue_count"])) | |||
| dateRecord.EncyclopediasCount = getInt(string(row["encyclopedias_count"])) | |||
| dateRecord.CreateRepoCount = getInt(string(row["create_repo_count"])) | |||
| dateRecord.LoginCount = getInt(string(row["login_count"])) | |||
| } | |||
| } | |||
| return dateRecord | |||
| } | |||
| func getInt(str string) int { | |||
| re, err := strconv.ParseInt(str, 10, 32) | |||
| if err != nil { | |||
| return 0 | |||
| } | |||
| return int(re) | |||
| } | |||
| func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { | |||
| CounDataByDateAndReCount(wikiCountMap, startTime, endTime, false) | |||
| } | |||
| func querySolveIssue(start_unix int64, end_unix int64) map[int64]int { | |||
| @@ -299,6 +674,24 @@ func queryPullRequest(start_unix int64, end_unix int64) map[int64]int { | |||
| return resultMap | |||
| } | |||
| func queryCommitAction(start_unix int64, end_unix int64, actionType int64) map[int64]int { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| sess.Select("id,user_id,op_type,act_user_id").Table("action").Where("user_id=act_user_id and op_type=" + fmt.Sprint(actionType) + " and created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)) | |||
| actionList := make([]*Action, 0) | |||
| sess.Find(&actionList) | |||
| resultMap := make(map[int64]int) | |||
| log.Info("query action size=" + fmt.Sprint(len(actionList))) | |||
| for _, actionRecord := range actionList { | |||
| if _, ok := resultMap[actionRecord.UserID]; !ok { | |||
| resultMap[actionRecord.UserID] = 1 | |||
| } else { | |||
| resultMap[actionRecord.UserID] += 1 | |||
| } | |||
| } | |||
| return resultMap | |||
| } | |||
| func queryAction(start_unix int64, end_unix int64, actionType int64) map[int64]int { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| @@ -386,10 +779,10 @@ func queryFollow(start_unix int64, end_unix int64) map[int64]int { | |||
| resultMap := make(map[int64]int) | |||
| log.Info("query Follow size=" + fmt.Sprint(len(followList))) | |||
| for _, followRecord := range followList { | |||
| if _, ok := resultMap[followRecord.UserID]; !ok { | |||
| resultMap[followRecord.UserID] = 1 | |||
| if _, ok := resultMap[followRecord.FollowID]; !ok { | |||
| resultMap[followRecord.FollowID] = 1 | |||
| } else { | |||
| resultMap[followRecord.UserID] += 1 | |||
| resultMap[followRecord.FollowID] += 1 | |||
| } | |||
| } | |||
| return resultMap | |||
| @@ -432,6 +825,62 @@ func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | |||
| return resultMap | |||
| } | |||
| func queryUserRepoOpenIIndex(start_unix int64, end_unix int64) map[int64]float64 { | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| statictisSess.Select("repo_id,radar_total").Table("repo_statistic").Where("created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)) | |||
| repoStatisticList := make([]*RepoStatistic, 0) | |||
| statictisSess.Find(&repoStatisticList) | |||
| repoOpenIIndexMap := make(map[int64]float64) | |||
| log.Info("query repo_statistic size=" + fmt.Sprint(len(repoStatisticList))) | |||
| for _, repoRecord := range repoStatisticList { | |||
| if _, ok := repoOpenIIndexMap[repoRecord.RepoID]; !ok { | |||
| repoOpenIIndexMap[repoRecord.RepoID] = repoRecord.RadarTotal | |||
| } | |||
| } | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| sess.Select("id,owner_id,name").Table("repository").Where("is_fork=false") | |||
| repoList := make([]*Repository, 0) | |||
| sess.Find(&repoList) | |||
| userMap := make(map[int64]float64) | |||
| log.Info("query Repository size=" + fmt.Sprint(len(repoList))) | |||
| for _, repoRecord := range repoList { | |||
| if _, ok := userMap[repoRecord.OwnerID]; !ok { | |||
| if _, ok := repoOpenIIndexMap[repoRecord.ID]; ok { | |||
| userMap[repoRecord.OwnerID] = repoOpenIIndexMap[repoRecord.ID] | |||
| } | |||
| } | |||
| } | |||
| //query collaboration | |||
| sess.Select("repo_id,user_id,mode").Table("collaboration") | |||
| collaborationList := make([]*Collaboration, 0) | |||
| sess.Find(&collaborationList) | |||
| log.Info("query collaborationList size=" + fmt.Sprint(len(collaborationList))) | |||
| for _, collaborationRecord := range collaborationList { | |||
| if _, ok := userMap[collaborationRecord.UserID]; !ok { | |||
| if _, ok := repoOpenIIndexMap[collaborationRecord.RepoID]; ok { | |||
| userMap[collaborationRecord.UserID] = repoOpenIIndexMap[collaborationRecord.RepoID] | |||
| } | |||
| } else { | |||
| if _, ok := repoOpenIIndexMap[collaborationRecord.RepoID]; ok { | |||
| userMap[collaborationRecord.UserID] += repoOpenIIndexMap[collaborationRecord.RepoID] | |||
| } | |||
| } | |||
| } | |||
| userMapJson, _ := json.Marshal(userMap) | |||
| log.Info("userMapJson=" + string(userMapJson)) | |||
| return userMap | |||
| } | |||
| func queryLoginCount(start_unix int64, end_unix int64) map[int64]int { | |||
| statictisSess := xStatistic.NewSession() | |||
| defer statictisSess.Close() | |||
| @@ -470,5 +919,8 @@ func subMonth(t1, t2 time.Time) (month int) { | |||
| } | |||
| monthInterval %= 12 | |||
| month = yearInterval*12 + monthInterval | |||
| if month == 0 { | |||
| month = 1 | |||
| } | |||
| return month | |||
| } | |||
| @@ -19,6 +19,7 @@ type CreateModelArtsNotebookForm struct { | |||
| JobName string `form:"job_name" binding:"Required"` | |||
| Attachment string `form:"attachment"` | |||
| Description string `form:"description"` | |||
| Flavor string `form:"flavor"` | |||
| } | |||
| func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| @@ -38,6 +39,10 @@ type CreateModelArtsTrainJobForm struct { | |||
| IsSaveParam string `form:"is_save_para"` | |||
| ParameterTemplateName string `form:"parameter_template_name"` | |||
| PrameterDescription string `form:"parameter_description"` | |||
| BranchName string `form:"branch_name" binding:"Required"` | |||
| VersionName string `form:"version_name" binding:"Required"` | |||
| FlavorName string `form:"flaver_names" binding:"Required"` | |||
| EngineName string `form:"engine_names" binding:"Required"` | |||
| } | |||
| func (f *CreateModelArtsTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| @@ -163,13 +163,13 @@ func registerHandleBlockChainUnSuccessCommits() { | |||
| }) | |||
| } | |||
| func registerHandleRepoStatistic() { | |||
| RegisterTaskFatal("handle_repo_statistic", &BaseConfig{ | |||
| func registerHandleRepoAndUserStatistic() { | |||
| RegisterTaskFatal("handle_repo_and_user_statistic", &BaseConfig{ | |||
| Enabled: true, | |||
| RunAtStart: false, | |||
| Schedule: "@daily", | |||
| }, func(ctx context.Context, _ *models.User, _ Config) error { | |||
| repo.RepoStatisticAuto() | |||
| repo.StatisticAuto() | |||
| return nil | |||
| }) | |||
| } | |||
| @@ -184,16 +184,6 @@ func registerHandleSummaryStatistic() { | |||
| return nil | |||
| }) | |||
| } | |||
| func registerHandleUserStatistic() { | |||
| RegisterTaskFatal("handle_user_statistic", &BaseConfig{ | |||
| Enabled: true, | |||
| RunAtStart: false, | |||
| Schedule: "@daily", | |||
| }, func(ctx context.Context, _ *models.User, _ Config) error { | |||
| repo.TimingCountData() | |||
| return nil | |||
| }) | |||
| } | |||
| func initBasicTasks() { | |||
| registerUpdateMirrorTask() | |||
| @@ -210,7 +200,6 @@ func initBasicTasks() { | |||
| registerHandleBlockChainMergedPulls() | |||
| registerHandleBlockChainUnSuccessCommits() | |||
| registerHandleRepoStatistic() | |||
| registerHandleUserStatistic() | |||
| registerHandleRepoAndUserStatistic() | |||
| registerHandleSummaryStatistic() | |||
| } | |||
| @@ -32,7 +32,7 @@ func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (strin | |||
| if tmpRemote != "origin" { | |||
| tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base | |||
| // Fetch commit into a temporary branch in order to be able to handle commits and tags | |||
| _, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path) | |||
| _, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName,"--no-tags").RunInDir(repo.Path) | |||
| if err == nil { | |||
| base = tmpBaseName | |||
| } | |||
| @@ -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 | |||
| @@ -2,6 +2,7 @@ package modelarts | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "path" | |||
| "strconv" | |||
| @@ -35,19 +36,24 @@ const ( | |||
| // "{\"code\":\"modelarts.bm.910.arm.public.4\",\"value\":\"Ascend : 4 * Ascend 910 CPU:96 核 1024GiB\"}," + | |||
| // "{\"code\":\"modelarts.bm.910.arm.public.1\",\"value\":\"Ascend : 1 * Ascend 910 CPU:24 核 256GiB\"}" + | |||
| // "]}" | |||
| CodePath = "/code/" | |||
| OutputPath = "/output/" | |||
| LogPath = "/log/" | |||
| JobPath = "/job/" | |||
| OrderDesc = "desc" //向下查询 | |||
| OrderAsc = "asc" //向上查询 | |||
| Lines = 20 | |||
| TrainUrl = "train_url" | |||
| DataUrl = "data_url" | |||
| PerPage = 10 | |||
| SortByCreateTime = "create_time" | |||
| ConfigTypeCustom = "custom" | |||
| CodePath = "/code/" | |||
| OutputPath = "/output/" | |||
| LogPath = "/log/" | |||
| JobPath = "/job/" | |||
| OrderDesc = "desc" //向下查询 | |||
| OrderAsc = "asc" //向上查询 | |||
| Lines = 500 | |||
| TrainUrl = "train_url" | |||
| DataUrl = "data_url" | |||
| PerPage = 10 | |||
| IsLatestVersion = "1" | |||
| NotLatestVersion = "0" | |||
| ComputeResource = "NPU" | |||
| VersionCount = 1 | |||
| SortByCreateTime = "create_time" | |||
| ConfigTypeCustom = "custom" | |||
| TotalVersionCount = 1 | |||
| ) | |||
| var ( | |||
| @@ -56,19 +62,55 @@ var ( | |||
| ) | |||
| type GenerateTrainJobReq struct { | |||
| JobName string | |||
| Uuid string | |||
| Description string | |||
| CodeObsPath string | |||
| BootFile string | |||
| DataUrl string | |||
| TrainUrl string | |||
| FlavorCode string | |||
| LogUrl string | |||
| PoolID string | |||
| WorkServerNumber int | |||
| EngineID int64 | |||
| Parameters []models.Parameter | |||
| JobName string | |||
| Uuid string | |||
| Description string | |||
| CodeObsPath string | |||
| BootFile string | |||
| BootFileUrl string | |||
| DataUrl string | |||
| TrainUrl string | |||
| FlavorCode string | |||
| LogUrl string | |||
| PoolID string | |||
| WorkServerNumber int | |||
| EngineID int64 | |||
| Parameters []models.Parameter | |||
| CommitID string | |||
| IsLatestVersion string | |||
| Params string | |||
| BranchName string | |||
| PreVersionId int64 | |||
| PreVersionName string | |||
| FlavorName string | |||
| VersionCount int | |||
| EngineName string | |||
| TotalVersionCount int | |||
| } | |||
| type GenerateTrainJobVersionReq struct { | |||
| JobName string | |||
| Uuid string | |||
| Description string | |||
| CodeObsPath string | |||
| BootFile string | |||
| BootFileUrl string | |||
| DataUrl string | |||
| TrainUrl string | |||
| FlavorCode string | |||
| LogUrl string | |||
| PoolID string | |||
| WorkServerNumber int | |||
| EngineID int64 | |||
| Parameters []models.Parameter | |||
| Params string | |||
| PreVersionId int64 | |||
| CommitID string | |||
| BranchName string | |||
| FlavorName string | |||
| EngineName string | |||
| PreVersionName string | |||
| TotalVersionCount int | |||
| } | |||
| type VersionInfo struct { | |||
| @@ -99,7 +141,23 @@ type ResourcePool struct { | |||
| } `json:"resource_pool"` | |||
| } | |||
| func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | |||
| // type Parameter struct { | |||
| // Label string `json:"label"` | |||
| // Value string `json:"value"` | |||
| // } | |||
| // type Parameters struct { | |||
| // Parameter []Parameter `json:"parameter"` | |||
| // } | |||
| type Parameters struct { | |||
| Parameter []struct { | |||
| Label string `json:"label"` | |||
| Value string `json:"value"` | |||
| } `json:"parameter"` | |||
| } | |||
| func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor string) error { | |||
| var dataActualPath string | |||
| if uuid != "" { | |||
| dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | |||
| @@ -128,7 +186,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||
| JobName: jobName, | |||
| Description: description, | |||
| ProfileID: setting.ProfileID, | |||
| Flavor: setting.Flavor, | |||
| Flavor: flavor, | |||
| Pool: models.Pool{ | |||
| ID: poolInfos.PoolInfo[0].PoolId, | |||
| Name: poolInfos.PoolInfo[0].PoolName, | |||
| @@ -170,14 +228,14 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||
| return nil | |||
| } | |||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | |||
| jobResult, err := createTrainJob(models.CreateTrainJobParams{ | |||
| JobName: req.JobName, | |||
| Description: req.Description, | |||
| Config: models.Config{ | |||
| WorkServerNum: req.WorkServerNumber, | |||
| AppUrl: req.CodeObsPath, | |||
| BootFileUrl: req.BootFile, | |||
| BootFileUrl: req.BootFileUrl, | |||
| DataUrl: req.DataUrl, | |||
| EngineID: req.EngineID, | |||
| TrainUrl: req.TrainUrl, | |||
| @@ -198,21 +256,38 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||
| attach, err := models.GetAttachmentByUUID(req.Uuid) | |||
| if err != nil { | |||
| log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | |||
| return nil | |||
| return err | |||
| } | |||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
| Status: TransTrainJobStatus(jobResult.Status), | |||
| UserID: ctx.User.ID, | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| JobID: strconv.FormatInt(jobResult.JobID, 10), | |||
| JobName: req.JobName, | |||
| JobType: string(models.JobTypeTrain), | |||
| Type: models.TypeCloudBrainTwo, | |||
| VersionID: jobResult.VersionID, | |||
| VersionName: jobResult.VersionName, | |||
| Uuid: req.Uuid, | |||
| DatasetName: attach.Name, | |||
| Status: TransTrainJobStatus(jobResult.Status), | |||
| UserID: ctx.User.ID, | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| JobID: strconv.FormatInt(jobResult.JobID, 10), | |||
| JobName: req.JobName, | |||
| JobType: string(models.JobTypeTrain), | |||
| Type: models.TypeCloudBrainTwo, | |||
| VersionID: jobResult.VersionID, | |||
| VersionName: jobResult.VersionName, | |||
| Uuid: req.Uuid, | |||
| DatasetName: attach.Name, | |||
| CommitID: req.CommitID, | |||
| IsLatestVersion: req.IsLatestVersion, | |||
| ComputeResource: ComputeResource, | |||
| EngineID: req.EngineID, | |||
| TrainUrl: req.TrainUrl, | |||
| BranchName: req.BranchName, | |||
| Parameters: req.Params, | |||
| BootFile: req.BootFile, | |||
| DataUrl: req.DataUrl, | |||
| LogUrl: req.LogUrl, | |||
| FlavorCode: req.FlavorCode, | |||
| Description: req.Description, | |||
| WorkServerNumber: req.WorkServerNumber, | |||
| FlavorName: req.FlavorName, | |||
| EngineName: req.EngineName, | |||
| VersionCount: req.VersionCount, | |||
| TotalVersionCount: req.TotalVersionCount, | |||
| }) | |||
| if err != nil { | |||
| @@ -223,6 +298,96 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||
| return nil | |||
| } | |||
| func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) { | |||
| jobResult, err := createTrainJobVersion(models.CreateTrainJobVersionParams{ | |||
| Description: req.Description, | |||
| Config: models.TrainJobVersionConfig{ | |||
| WorkServerNum: req.WorkServerNumber, | |||
| AppUrl: req.CodeObsPath, | |||
| BootFileUrl: req.BootFileUrl, | |||
| DataUrl: req.DataUrl, | |||
| EngineID: req.EngineID, | |||
| TrainUrl: req.TrainUrl, | |||
| LogUrl: req.LogUrl, | |||
| PoolID: req.PoolID, | |||
| Flavor: models.Flavor{ | |||
| Code: req.FlavorCode, | |||
| }, | |||
| Parameter: req.Parameters, | |||
| PreVersionId: req.PreVersionId, | |||
| }, | |||
| }, jobId) | |||
| if err != nil { | |||
| log.Error("CreateJob failed: %v", err.Error()) | |||
| return err | |||
| } | |||
| attach, err := models.GetAttachmentByUUID(req.Uuid) | |||
| if err != nil { | |||
| log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | |||
| return err | |||
| } | |||
| repo := ctx.Repo.Repository | |||
| VersionTaskList, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{ | |||
| RepoID: repo.ID, | |||
| Type: models.TypeCloudBrainTwo, | |||
| JobType: string(models.JobTypeTrain), | |||
| JobID: strconv.FormatInt(jobResult.JobID, 10), | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("Cloudbrain", err) | |||
| return err | |||
| } | |||
| //将当前版本的isLatestVersion设置为"1"和任务数量更新,任务数量包括当前版本数VersionCount和历史创建的总版本数TotalVersionCount | |||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
| Status: TransTrainJobStatus(jobResult.Status), | |||
| UserID: ctx.User.ID, | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| JobID: strconv.FormatInt(jobResult.JobID, 10), | |||
| JobName: req.JobName, | |||
| JobType: string(models.JobTypeTrain), | |||
| Type: models.TypeCloudBrainTwo, | |||
| VersionID: jobResult.VersionID, | |||
| VersionName: jobResult.VersionName, | |||
| Uuid: req.Uuid, | |||
| DatasetName: attach.Name, | |||
| CommitID: req.CommitID, | |||
| IsLatestVersion: req.IsLatestVersion, | |||
| PreVersionName: req.PreVersionName, | |||
| ComputeResource: ComputeResource, | |||
| EngineID: req.EngineID, | |||
| TrainUrl: req.TrainUrl, | |||
| BranchName: req.BranchName, | |||
| Parameters: req.Params, | |||
| BootFile: req.BootFile, | |||
| DataUrl: req.DataUrl, | |||
| LogUrl: req.LogUrl, | |||
| PreVersionId: req.PreVersionId, | |||
| FlavorCode: req.FlavorCode, | |||
| Description: req.Description, | |||
| WorkServerNumber: req.WorkServerNumber, | |||
| FlavorName: req.FlavorName, | |||
| EngineName: req.EngineName, | |||
| TotalVersionCount: VersionTaskList[0].TotalVersionCount + 1, | |||
| VersionCount: VersionListCount + 1, | |||
| }) | |||
| if err != nil { | |||
| log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) | |||
| return err | |||
| } | |||
| //将训练任务的上一版本的isLatestVersion设置为"0" | |||
| err = models.SetVersionCountAndLatestVersion(strconv.FormatInt(jobResult.JobID, 10), VersionTaskList[0].VersionName, VersionCount, NotLatestVersion, TotalVersionCount) | |||
| if err != nil { | |||
| ctx.ServerError("Update IsLatestVersion failed", err) | |||
| return err | |||
| } | |||
| return err | |||
| } | |||
| func TransTrainJobStatus(status int) string { | |||
| switch status { | |||
| case 0: | |||
| @@ -273,6 +438,10 @@ func TransTrainJobStatus(status int) string { | |||
| default: | |||
| return strconv.Itoa(status) | |||
| } | |||
| } | |||
| return "" | |||
| func GetVersionOutputPathByTotalVersionCount(TotalVersionCount int) (VersionOutputPath string) { | |||
| talVersionCountToString := fmt.Sprintf("%04d", TotalVersionCount) | |||
| VersionOutputPath = "V" + talVersionCountToString | |||
| return VersionOutputPath | |||
| } | |||
| @@ -366,6 +366,16 @@ sendjob: | |||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
| } | |||
| log.Error("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| BootFileErrorMsg := "Invalid OBS path '" + createJobParams.Config.BootFileUrl + "'." | |||
| DataSetErrorMsg := "Invalid OBS path '" + createJobParams.Config.DataUrl + "'." | |||
| if temp.ErrorMsg == BootFileErrorMsg { | |||
| log.Error("启动文件错误!createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| return &result, fmt.Errorf("启动文件错误!") | |||
| } | |||
| if temp.ErrorMsg == DataSetErrorMsg { | |||
| log.Error("数据集错误!createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| return &result, fmt.Errorf("数据集错误!") | |||
| } | |||
| return &result, fmt.Errorf("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| } | |||
| @@ -377,6 +387,61 @@ sendjob: | |||
| return &result, nil | |||
| } | |||
| func createTrainJobVersion(createJobVersionParams models.CreateTrainJobVersionParams, jobID string) (*models.CreateTrainJobResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.CreateTrainJobResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetBody(createJobVersionParams). | |||
| SetResult(&result). | |||
| Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions") | |||
| if err != nil { | |||
| return nil, fmt.Errorf("resty create train-job version: %s", err) | |||
| } | |||
| req, _ := json.Marshal(createJobVersionParams) | |||
| log.Info("%s", req) | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| if res.StatusCode() != http.StatusOK { | |||
| var temp models.ErrorResult | |||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
| } | |||
| BootFileErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.BootFileUrl + "'." | |||
| DataSetErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.DataUrl + "'." | |||
| if temp.ErrorMsg == BootFileErrorMsg { | |||
| log.Error("启动文件错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| return &result, fmt.Errorf("启动文件错误!") | |||
| } | |||
| if temp.ErrorMsg == DataSetErrorMsg { | |||
| log.Error("数据集错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| return &result, fmt.Errorf("数据集错误!") | |||
| } | |||
| return &result, fmt.Errorf("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| } | |||
| if !result.IsSuccess { | |||
| log.Error("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| return &result, fmt.Errorf("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||
| } | |||
| return &result, nil | |||
| } | |||
| func GetResourceSpecs() (*models.GetResourceSpecsResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| @@ -768,3 +833,44 @@ sendjob: | |||
| return &result, nil | |||
| } | |||
| func DelTrainJobVersion(jobID string, versionID string) (*models.TrainJobResult, error) { | |||
| checkSetting() | |||
| client := getRestyClient() | |||
| var result models.TrainJobResult | |||
| retry := 0 | |||
| sendjob: | |||
| res, err := client.R(). | |||
| SetAuthToken(TOKEN). | |||
| SetResult(&result). | |||
| Delete(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID) | |||
| if err != nil { | |||
| return &result, fmt.Errorf("resty DelTrainJobVersion: %v", err) | |||
| } | |||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
| retry++ | |||
| _ = getToken() | |||
| goto sendjob | |||
| } | |||
| if res.StatusCode() != http.StatusOK { | |||
| var temp models.ErrorResult | |||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
| } | |||
| log.Error("DelTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| return &result, fmt.Errorf("删除训练作业版本失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
| } | |||
| if !result.IsSuccess { | |||
| log.Error("DelTrainJob(%s) failed", jobID) | |||
| return &result, fmt.Errorf("删除训练作业版本失败:%s", result.ErrorMsg) | |||
| } | |||
| return &result, nil | |||
| } | |||
| @@ -4,6 +4,7 @@ import ( | |||
| "bytes" | |||
| "encoding/base64" | |||
| "encoding/json" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "net/http" | |||
| @@ -113,13 +114,23 @@ func GetResultFromElk(resultInfo ResultInfo, jsonStr []byte) (loaded int, totalV | |||
| client := &http.Client{} | |||
| resp, err := client.Do(req) | |||
| if err != nil { | |||
| panic(err) | |||
| // panic(err) | |||
| return 0, 0, err | |||
| } | |||
| defer resp.Body.Close() | |||
| body, _ := ioutil.ReadAll(resp.Body) | |||
| errs := json.Unmarshal([]byte(string(body)), &resultInfo) | |||
| log.Info("Get resultJson failed", errs) | |||
| if resp.StatusCode != 200 { | |||
| log.Error("ConnectToElk failed:%s", resp.Status) | |||
| return 0, 0, fmt.Errorf("ConnectToElk failed:%s", resp.Status) | |||
| } | |||
| body, err := ioutil.ReadAll(resp.Body) | |||
| if err != nil { | |||
| return 0, 0, err | |||
| } | |||
| err = json.Unmarshal([]byte(string(body)), &resultInfo) | |||
| if err != nil { | |||
| log.Error("Get resultJson failed", err) | |||
| return 0, 0, err | |||
| } | |||
| return resultInfo.Result.Loaded, resultInfo.Result.RawResponse.Hits.Total, err | |||
| } | |||
| @@ -138,6 +149,7 @@ func ProjectViewInit(User string, Project string, Gte string, Lte string) (proje | |||
| var timeRange Range | |||
| timeRange.Timestamptest.Gte = Gte | |||
| timeRange.Timestamptest.Lte = Lte | |||
| timeRange.Timestamptest.Format = "strict_date_optional_time" | |||
| inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Range = &timeRange | |||
| //限定用户 | |||
| var userName FilterMatchPhrase | |||
| @@ -208,9 +220,10 @@ func TagNameInit(MessageInfo string, Tagname string, Gte string, Lte string) (pr | |||
| //向elk发送请求,将获取的结果只保留访问量,输入是初始化后的数据结构,返回访问量 | |||
| func ViewInfo(viewInfo InputInfo) (totalView int, err error) { | |||
| jsons, errs := json.Marshal(viewInfo) | |||
| if errs != nil { | |||
| log.Info("errs:", errs) | |||
| jsons, err := json.Marshal(viewInfo) | |||
| if err != nil { | |||
| log.Error("jsons failed", err) | |||
| return 0, err | |||
| } | |||
| var jsonStr = []byte(jsons) | |||
| var resultInfo ResultInfo | |||
| @@ -221,7 +234,6 @@ func ViewInfo(viewInfo InputInfo) (totalView int, err error) { | |||
| if loaded == 0 { | |||
| loaded_next, totalView, err := GetResultFromElk(resultInfo, jsonStr) | |||
| time++ | |||
| log.Info("time:", time) | |||
| if loaded_next != 0 && time < 100 { | |||
| return totalView, err | |||
| } | |||
| @@ -163,6 +163,7 @@ var ( | |||
| // UI settings | |||
| UI = struct { | |||
| ExplorePagingNum int | |||
| ContributorPagingNum int | |||
| IssuePagingNum int | |||
| RepoSearchPagingNum int | |||
| MembersPagingNum int | |||
| @@ -203,19 +204,20 @@ var ( | |||
| Keywords string | |||
| } `ini:"ui.meta"` | |||
| }{ | |||
| ExplorePagingNum: 20, | |||
| IssuePagingNum: 10, | |||
| RepoSearchPagingNum: 10, | |||
| MembersPagingNum: 20, | |||
| FeedMaxCommitNum: 5, | |||
| GraphMaxCommitNum: 100, | |||
| CodeCommentLines: 4, | |||
| ReactionMaxUserNum: 10, | |||
| ThemeColorMetaTag: `#6cc644`, | |||
| MaxDisplayFileSize: 8388608, | |||
| DefaultTheme: `gitea`, | |||
| Themes: []string{`gitea`, `arc-green`}, | |||
| Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, | |||
| ExplorePagingNum: 20, | |||
| ContributorPagingNum: 50, | |||
| IssuePagingNum: 10, | |||
| RepoSearchPagingNum: 10, | |||
| MembersPagingNum: 20, | |||
| FeedMaxCommitNum: 5, | |||
| GraphMaxCommitNum: 100, | |||
| CodeCommentLines: 4, | |||
| ReactionMaxUserNum: 10, | |||
| ThemeColorMetaTag: `#6cc644`, | |||
| MaxDisplayFileSize: 8388608, | |||
| DefaultTheme: `gitea`, | |||
| Themes: []string{`gitea`, `arc-green`}, | |||
| Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, | |||
| Notification: struct { | |||
| MinTimeout time.Duration | |||
| TimeoutStep time.Duration | |||
| @@ -544,7 +546,11 @@ var ( | |||
| GrowthContributors float64 | |||
| GrowthCommit float64 | |||
| GrowthComments float64 | |||
| RecordBeginTime string | |||
| IgnoreMirrorRepo bool | |||
| }{} | |||
| Warn_Notify_Mails []string | |||
| ) | |||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | |||
| @@ -1288,6 +1294,9 @@ func NewContext() { | |||
| ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | |||
| SetRadarMapConfig() | |||
| sec = Cfg.Section("warn_mail") | |||
| Warn_Notify_Mails = strings.Split(sec.Key("mails").MustString(""), ",") | |||
| } | |||
| func SetRadarMapConfig() { | |||
| @@ -1324,6 +1333,8 @@ func SetRadarMapConfig() { | |||
| RadarMap.GrowthContributors = sec.Key("growth_contributors").MustFloat64(0.2) | |||
| 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) | |||
| } | |||
| @@ -6,15 +6,17 @@ package storage | |||
| import ( | |||
| "io" | |||
| "net/url" | |||
| "path" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "github.com/unknwon/com" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/obs" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "github.com/unknwon/com" | |||
| ) | |||
| type FileInfo struct { | |||
| @@ -27,20 +29,20 @@ type FileInfo struct { | |||
| } | |||
| //check if has the object | |||
| //todo:修改查询方式 | |||
| func ObsHasObject(path string) (bool, error) { | |||
| hasObject := false | |||
| output, err := ObsCli.ListObjects(&obs.ListObjectsInput{Bucket: setting.Bucket}) | |||
| if err != nil { | |||
| log.Error("ListObjects failed:%v", err) | |||
| return hasObject, err | |||
| } | |||
| for _, obj := range output.Contents { | |||
| //obj.Key:attachment/0/1/019fd24e-4ef7-41cc-9f85-4a7b8504d958 | |||
| if path == obj.Key { | |||
| hasObject = true | |||
| break | |||
| input := &obs.GetObjectMetadataInput{} | |||
| input.Bucket = setting.Bucket | |||
| input.Key = path | |||
| _, err := ObsCli.GetObjectMetadata(input) | |||
| if err == nil { | |||
| hasObject = true | |||
| } else { | |||
| if obsError, ok := err.(obs.ObsError); ok { | |||
| log.Error("GetObjectMetadata failed(%d): %s", obsError.StatusCode, obsError.Message) | |||
| } else { | |||
| log.Error("%v", err.Error()) | |||
| } | |||
| } | |||
| @@ -174,38 +176,56 @@ func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) { | |||
| } | |||
| } | |||
| func GetObsListObject(jobName, parentDir string) ([]FileInfo, error) { | |||
| func GetObsListObject(jobName, parentDir, versionName string) ([]FileInfo, error) { | |||
| input := &obs.ListObjectsInput{} | |||
| input.Bucket = setting.Bucket | |||
| input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/") | |||
| input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, versionName, parentDir), "/") | |||
| strPrefix := strings.Split(input.Prefix, "/") | |||
| output, err := ObsCli.ListObjects(input) | |||
| fileInfos := make([]FileInfo, 0) | |||
| if err == nil { | |||
| for _, val := range output.Contents { | |||
| str1 := strings.Split(val.Key, "/") | |||
| var isDir bool | |||
| var fileName,nextParentDir string | |||
| var fileName, nextParentDir string | |||
| if strings.HasSuffix(val.Key, "/") { | |||
| //dirs in next level dir | |||
| if len(str1)-len(strPrefix) > 2 { | |||
| continue | |||
| } | |||
| fileName = str1[len(str1)-2] | |||
| isDir = true | |||
| nextParentDir = fileName | |||
| if fileName == parentDir || (fileName + "/") == setting.OutPutPath { | |||
| if parentDir == "" { | |||
| nextParentDir = fileName | |||
| } else { | |||
| nextParentDir = parentDir + "/" + fileName | |||
| } | |||
| if fileName == strPrefix[len(strPrefix)-1] || (fileName+"/") == setting.OutPutPath { | |||
| continue | |||
| } | |||
| } else { | |||
| //files in next level dir | |||
| if len(str1)-len(strPrefix) > 1 { | |||
| continue | |||
| } | |||
| fileName = str1[len(str1)-1] | |||
| isDir = false | |||
| nextParentDir = parentDir | |||
| } | |||
| fileInfo := FileInfo{ | |||
| ModTime: val.LastModified.Format("2006-01-02 15:04:05"), | |||
| ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"), | |||
| FileName: fileName, | |||
| Size: val.Size, | |||
| IsDir:isDir, | |||
| Size: val.Size, | |||
| IsDir: isDir, | |||
| ParenDir: nextParentDir, | |||
| } | |||
| fileInfos = append(fileInfos, fileInfo) | |||
| } | |||
| sort.Slice(fileInfos, func(i, j int) bool { | |||
| return fileInfos[i].ModTime > fileInfos[j].ModTime | |||
| }) | |||
| return fileInfos, err | |||
| } else { | |||
| if obsError, ok := err.(obs.ObsError); ok { | |||
| @@ -242,11 +262,12 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) | |||
| input := &obs.CreateSignedUrlInput{} | |||
| input.Bucket = setting.Bucket | |||
| input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/") | |||
| input.Expires = 60 * 60 | |||
| input.Method = obs.HttpMethodGet | |||
| reqParams := make(map[string]string) | |||
| fileName = url.QueryEscape(fileName) | |||
| reqParams["response-content-disposition"] = "attachment; filename=\"" + fileName + "\"" | |||
| input.QueryParams = reqParams | |||
| output, err := ObsCli.CreateSignedUrl(input) | |||
| @@ -254,8 +275,34 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) | |||
| log.Error("CreateSignedUrl failed:", err.Error()) | |||
| return "", err | |||
| } | |||
| log.Info("SignedUrl:%s", output.SignedUrl) | |||
| return output.SignedUrl, nil | |||
| } | |||
| func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) { | |||
| input := &obs.CreateSignedUrlInput{} | |||
| input.Bucket = bucket | |||
| input.Key = key | |||
| input.Expires = 60 * 60 | |||
| input.Method = obs.HttpMethodGet | |||
| comma := strings.LastIndex(key, "/") | |||
| filename := key | |||
| if comma != -1 { | |||
| filename = key[comma+1:] | |||
| } | |||
| reqParams := make(map[string]string) | |||
| filename = url.QueryEscape(filename) | |||
| reqParams["response-content-disposition"] = "attachment; filename=\"" + filename + "\"" | |||
| input.QueryParams = reqParams | |||
| output, err := ObsCli.CreateSignedUrl(input) | |||
| if err != nil { | |||
| log.Error("CreateSignedUrl failed:", err.Error()) | |||
| return "", err | |||
| } | |||
| return output.SignedUrl, nil | |||
| } | |||
| func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | |||
| @@ -92,6 +92,7 @@ func NewFuncMap() []template.FuncMap { | |||
| "Str2html": Str2html, | |||
| "TimeSince": timeutil.TimeSince, | |||
| "TimeSinceUnix": timeutil.TimeSinceUnix, | |||
| "TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||
| "RawTimeSince": timeutil.RawTimeSince, | |||
| "FileSize": base.FileSize, | |||
| "PrettyNumber": base.PrettyNumber, | |||
| @@ -340,6 +341,7 @@ func NewTextFuncMap() []texttmpl.FuncMap { | |||
| }, | |||
| "TimeSince": timeutil.TimeSince, | |||
| "TimeSinceUnix": timeutil.TimeSinceUnix, | |||
| "TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||
| "RawTimeSince": timeutil.RawTimeSince, | |||
| "DateFmtLong": func(t time.Time) string { | |||
| return t.Format(time.RFC1123Z) | |||
| @@ -162,3 +162,8 @@ func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML { | |||
| then.FormatInLocation(GetTimeFormat(lang), setting.DefaultUILocation), | |||
| timeSinceUnix(int64(then), int64(now), lang))) | |||
| } | |||
| func TimeSinceUnix1(then TimeStamp) string { | |||
| format := time.Unix(int64(then), 0).Format("2006-01-02 15:04:05") | |||
| return format | |||
| } | |||
| @@ -218,6 +218,7 @@ show_only_private = Showing only private | |||
| show_only_public = Showing only public | |||
| issues.in_your_repos = In your repositories | |||
| contributors = Contributors | |||
| [explore] | |||
| repos = Repositories | |||
| @@ -401,6 +402,26 @@ form.name_reserved = The username '%s' is reserved. | |||
| form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username. | |||
| form.name_chars_not_allowed = User name '%s' contains invalid characters. | |||
| static.sheetname=User Analysis | |||
| static.id=ID | |||
| static.name=User Name | |||
| static.codemergecount=PR Count | |||
| static.commitcount=Commit Count | |||
| static.issuecount=Issue Count | |||
| static.commentcount=Comment Count | |||
| static.focusrepocount=Focus Repo Count | |||
| static.starrepocount=Repo Star Count | |||
| static.logincount=Login Count | |||
| static.watchedcount=Watched Count | |||
| static.commitcodesize=Commit Code Line | |||
| static.solveissuecount=Solve Issue Count | |||
| static.encyclopediascount=Encyclopedias Count | |||
| static.createrepocount=Create Repo Count | |||
| static.openiindex=OpenI Index | |||
| static.registdate=Regist Date | |||
| static.countdate=Count Date | |||
| static.all=All | |||
| [settings] | |||
| profile = Profile | |||
| account = Account | |||
| @@ -755,6 +776,7 @@ unit_disabled = The site administrator has disabled this repository section. | |||
| language_other = Other | |||
| datasets = Datasets | |||
| datasets.desc = Enable Dataset | |||
| cloudbrain_helper=Use GPU/NPU resources to open notebooks, model training tasks, etc. | |||
| debug=Debug | |||
| stop=Stop | |||
| @@ -785,7 +807,15 @@ cloudbrain_operate = Operate | |||
| cloudbrain_status_createtime = Status/Createtime | |||
| cloudbrain_status_runtime = Running Time | |||
| record_begintime_get_err=Can not get the record begin time. | |||
| parameter_is_wrong=The input parameter is wrong. | |||
| total_count_get_error=Can not get the total page. | |||
| last_update_time_error=Can not get the last updated time. | |||
| get_repo_stat_error=Can not get the statistics of the repository. | |||
| get_repo_info_error=Can not get the information of the repository. | |||
| generate_statistic_file_error=Fail to generate file. | |||
| repo_stat_inspect=ProjectAnalysis | |||
| all=All | |||
| modelarts.notebook=Debug Task | |||
| modelarts.train_job=Train Task | |||
| modelarts.train_job.new_debug= New Debug Task | |||
| @@ -793,7 +823,11 @@ modelarts.train_job.new_train=New Train Task | |||
| modelarts.train_job.config=Configuration information | |||
| modelarts.train_job.new=New train Task | |||
| modelarts.train_job.new_place=The description should not exceed 256 characters | |||
| modelarts.modify=Modify | |||
| modelarts.current_version=Current version | |||
| modelarts.parent_version=Parent Version | |||
| modelarts.run_version=Run Version | |||
| modelarts.train_job.compute_node=Compute Node | |||
| modelarts.train_job.basic_info=Basic Info | |||
| @@ -814,6 +848,8 @@ modelarts.train_job.AI_driver=AI Engine | |||
| modelarts.train_job.start_file=Start File | |||
| modelarts.train_job.boot_file_helper=The startup file is the entry file that your program executes, and it must be a file ending in .py | |||
| modelarts.train_job.dataset=Dataset | |||
| modelarts.code_version = Code Version | |||
| modelarts.parents_version = Parents Version | |||
| modelarts.train_job.run_parameter=Run Parameter | |||
| modelarts.train_job.add_run_parameter=Add Run Parameter | |||
| modelarts.train_job.parameter_name=Parameter Name | |||
| @@ -2152,6 +2188,19 @@ repos.stars = Stars | |||
| repos.forks = Forks | |||
| repos.issues = Issues | |||
| repos.size = Size | |||
| repos.id=ID | |||
| repos.projectName=Project Name | |||
| repos.isPrivate=Private | |||
| repos.openi=OpenI | |||
| repos.visit=Visit | |||
| repos.download=Code Download | |||
| repos.pr=PR | |||
| repos.commit=Commit | |||
| repos.closedIssues=Closed Issue | |||
| repos.contributor=Contributor | |||
| repos.yes=Yes | |||
| repos.no=No | |||
| datasets.dataset_manage_panel= Dataset Manage | |||
| datasets.owner=Owner | |||
| @@ -220,6 +220,8 @@ show_only_public=只显示公开的 | |||
| issues.in_your_repos=属于该用户项目的 | |||
| contributors=贡献者 | |||
| [explore] | |||
| repos=项目 | |||
| users=用户 | |||
| @@ -227,6 +229,7 @@ organizations=组织 | |||
| images = 云脑镜像 | |||
| search=搜索 | |||
| code=代码 | |||
| data_analysis=数字看板(内测) | |||
| repo_no_results=未找到匹配的项目。 | |||
| dataset_no_results = 未找到匹配的数据集。 | |||
| user_no_results=未找到匹配的用户。 | |||
| @@ -402,6 +405,25 @@ form.name_reserved='%s' 用户名被保留。 | |||
| form.name_pattern_not_allowed=用户名中不允许使用 "%s"。 | |||
| form.name_chars_not_allowed=用户名 '%s' 包含无效字符。 | |||
| static.sheetname=用户分析 | |||
| static.id=ID | |||
| static.name=用户名 | |||
| static.codemergecount=PR数 | |||
| static.commitcount=commit次数 | |||
| static.issuecount=提出任务数 | |||
| static.commentcount=评论数 | |||
| static.focusrepocount=关注项目数 | |||
| static.starrepocount=点赞项目数 | |||
| static.logincount=登录次数 | |||
| static.watchedcount=关注者数 | |||
| static.commitcodesize=commit代码行数 | |||
| static.solveissuecount=已解决任务数 | |||
| static.encyclopediascount=百科页面贡献次数 | |||
| static.createrepocount=创建项目数 | |||
| static.openiindex=OpenI指数 | |||
| static.registdate=用户注册时间 | |||
| static.countdate=系统统计时间 | |||
| static.all=所有 | |||
| [settings] | |||
| profile=个人信息 | |||
| account=账号 | |||
| @@ -757,6 +779,7 @@ unit_disabled=站点管理员已禁用此项目单元。 | |||
| language_other=其它 | |||
| datasets=数据集 | |||
| datasets.desc=数据集功能 | |||
| cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等 | |||
| debug=调试 | |||
| stop=停止 | |||
| @@ -787,13 +810,31 @@ cloudbrain_status_createtime=状态/创建时间 | |||
| cloudbrain_status_runtime = 运行时长 | |||
| cloudbrain_jobname_err=只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。 | |||
| record_begintime_get_err=无法获取统计开始时间。 | |||
| parameter_is_wrong=输入参数错误,请检查输入参数。 | |||
| total_count_get_error=查询总页数失败。 | |||
| last_update_time_error=查询最新更新时间失败。 | |||
| get_repo_stat_error=查询当前仓库的统计信息失败。 | |||
| get_repo_info_error=查询当前仓库信息失败。 | |||
| generate_statistic_file_error=生成文件失败。 | |||
| repo_stat_inspect=项目分析 | |||
| all=所有 | |||
| modelarts.status=状态 | |||
| modelarts.createtime=创建时间 | |||
| modelarts.version_nums=版本数 | |||
| modelarts.computing_resources=计算资源 | |||
| modelarts.notebook=调试任务 | |||
| modelarts.train_job=训练任务 | |||
| modelarts.train_job.new_debug=新建调试任务 | |||
| modelarts.train_job.new_train=新建训练任务 | |||
| modelarts.train_job.config=配置信息 | |||
| modelarts.train_job.new=新建训练任务 | |||
| modelarts.train_job.new_place=描述字数不超过256个字符 | |||
| modelarts.train_job.new_place=描述字数不超过255个字符 | |||
| modelarts.modify=修改 | |||
| modelarts.current_version=当前版本 | |||
| modelarts.parent_version=父版本 | |||
| modelarts.run_version=运行版本 | |||
| @@ -813,9 +854,14 @@ modelarts.train_job.frames=常用框架 | |||
| modelarts.train_job.algorithm_origin=算法来源 | |||
| modelarts.train_job.AI_driver=AI引擎 | |||
| modelarts.train_job.start_file=启动文件 | |||
| modelarts.train_job.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。 | |||
| modelarts.train_job.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如train.py、main.py、example/train.py、case/main.py。 | |||
| modelarts.train_job.boot_file_place=填写启动文件路径,默认为train.py | |||
| modelarts.train_job.dataset=数据集 | |||
| modelarts.code_version=代码分支 | |||
| modelarts.parents_version=基于版本 | |||
| modelarts.train_job.compute_node=计算节点 | |||
| modelarts.train_job.train_dataset=训练数据集 | |||
| modelarts.train_job.run_parameter=运行参数 | |||
| modelarts.train_job.add_run_parameter=增加运行参数 | |||
| modelarts.train_job.parameter_name=参数名 | |||
| @@ -2153,6 +2199,19 @@ repos.stars=点赞数 | |||
| repos.forks=派生数 | |||
| repos.issues=任务数 | |||
| repos.size=大小 | |||
| repos.id=ID | |||
| repos.projectName=项目名称 | |||
| repos.isPrivate=私有 | |||
| repos.openi=OpenI指数 | |||
| repos.visit=浏览量 | |||
| repos.download=代码下载量 | |||
| repos.pr=PR数 | |||
| repos.commit=Commit数 | |||
| repos.closedIssues=已解决任务数 | |||
| repos.contributor=贡献者数 | |||
| repos.yes=是 | |||
| repos.no=否 | |||
| datasets.dataset_manage_panel=数据集管理 | |||
| datasets.owner=所有者 | |||
| @@ -19,11 +19,13 @@ | |||
| "cssnano": "4.1.10", | |||
| "domino": "2.1.5", | |||
| "dropzone": "5.7.2", | |||
| "echarts": "3.8.5", | |||
| "element-ui": "2.15.5", | |||
| "esdk-obs-browserjs": "3.20.7", | |||
| "esdk-obs-nodejs": "3.20.11", | |||
| "fast-glob": "3.2.2", | |||
| "file-loader": "6.0.0", | |||
| "file-saver": "2.0.5", | |||
| "fomantic-ui": "2.8.4", | |||
| "fs": "0.0.1-security", | |||
| "highlight.js": "10.4.1", | |||
| @@ -55,13 +57,15 @@ | |||
| "webpack": "4.43.0", | |||
| "webpack-cli": "3.3.11", | |||
| "webpack-fix-style-only-entries": "0.4.0", | |||
| "worker-loader": "2.0.0" | |||
| "worker-loader": "2.0.0", | |||
| "xlsx": "0.17.3" | |||
| }, | |||
| "devDependencies": { | |||
| "eslint": "6.8.0", | |||
| "eslint-config-airbnb-base": "14.1.0", | |||
| "eslint-plugin-import": "2.20.2", | |||
| "eslint-plugin-vue": "6.2.2", | |||
| "script-loader": "0.7.2", | |||
| "stylelint": "13.3.3", | |||
| "stylelint-config-standard": "20.0.0", | |||
| "updates": "10.2.11" | |||
| @@ -0,0 +1,41 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | |||
| <svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | |||
| viewBox="0 0 96 17.3" style="enable-background:new 0 0 96 17.3;" xml:space="preserve"> | |||
| <style type="text/css"> | |||
| .st0{fill:#5BB973;} | |||
| </style> | |||
| <g> | |||
| <path class="st0" d="M5.9,12.5l-1,3.8h-3L6.6,1.5H10l4.7,14.8h-3.1l-1-3.8H5.9z M6.5,10.2H10L9.6,8.5C9.4,7.8,9.1,7,8.9,6.1 | |||
| C8.7,5.3,8.5,4.5,8.3,3.7H8.2C8,4.5,7.8,5.3,7.6,6.2S7.2,7.8,6.9,8.5L6.5,10.2z"/> | |||
| <path class="st0" d="M19.5,1.5v14.8h-3V1.5H19.5z"/> | |||
| <path class="st0" d="M27.7,6.6v10.6h-2.1V6.6h-1.8v-2h1.8V0.3h2.1v4.3h1.7v2H27.7z M31.6,7.4c-0.2,0.9-0.5,1.8-0.8,2.7 | |||
| c-0.3,0.9-0.7,1.6-1.1,2.2c-0.1-0.1-0.2-0.2-0.4-0.3s-0.3-0.2-0.4-0.3c-0.2-0.1-0.3-0.2-0.5-0.3c-0.2-0.1-0.3-0.2-0.4-0.2 | |||
| c0.4-0.5,0.7-1.1,1-1.9s0.5-1.5,0.6-2.3L31.6,7.4z M34.6,5.8c0,0.9-0.1,1.9-0.3,2.9c-0.1,1-0.4,2-0.7,3s-0.8,2-1.4,2.9 | |||
| c-0.6,0.9-1.4,1.8-2.4,2.6c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.3-0.3-0.4-0.4c-0.1-0.1-0.3-0.3-0.4-0.4s-0.3-0.2-0.4-0.3 | |||
| c0.9-0.7,1.6-1.5,2.1-2.3c0.6-0.8,1-1.7,1.3-2.5c0.3-0.9,0.5-1.7,0.7-2.6s0.2-1.7,0.3-2.5h-2.7V3.7h2.7V0.5h2v3.2h3.7 | |||
| c0,0.1,0,0.3,0,0.4c0,0.1,0,0.2,0,0.3s0,0.2,0,0.3l-0.1,2.7l1.3-0.3c0.1,0.4,0.3,0.9,0.4,1.3c0.1,0.5,0.3,0.9,0.4,1.4 | |||
| c0.1,0.5,0.2,0.9,0.3,1.3c0.1,0.4,0.2,0.8,0.2,1.1L39,12.8c-0.1-0.5-0.2-1.1-0.3-1.8s-0.3-1.4-0.5-2.1c0,1.2-0.1,2.1-0.1,3 | |||
| c0,0.8-0.1,1.5-0.2,2.1c-0.1,0.6-0.1,1-0.2,1.4c-0.1,0.3-0.2,0.6-0.3,0.8c-0.2,0.3-0.4,0.5-0.7,0.7c-0.2,0.1-0.5,0.2-0.8,0.3 | |||
| C35.6,17,35.3,17,34.8,17s-0.9,0-1.3,0c0-0.3-0.1-0.6-0.2-1c-0.1-0.4-0.3-0.7-0.4-1c0.4,0,0.9,0.1,1.2,0.1c0.4,0,0.7,0,0.8,0 | |||
| c0.2,0,0.3,0,0.4-0.1c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.2-0.4,0.3-0.8c0.1-0.4,0.1-0.9,0.2-1.6s0.1-1.6,0.1-2.7 | |||
| c0-1.1,0.1-2.4,0.1-3.9H34.6z"/> | |||
| <path class="st0" d="M47.8,1.1C47.6,1.7,47.3,2.4,47,3c-0.3,0.6-0.6,1.2-0.9,1.9v12.4h-2.2v-9c-0.2,0.2-0.4,0.5-0.6,0.7 | |||
| S43,9.3,42.8,9.5c0-0.1-0.1-0.3-0.2-0.5c-0.1-0.2-0.2-0.4-0.3-0.6c-0.1-0.2-0.2-0.4-0.3-0.6s-0.2-0.4-0.3-0.5 | |||
| c0.4-0.4,0.8-0.9,1.2-1.4c0.4-0.5,0.8-1.1,1.1-1.6c0.4-0.6,0.7-1.2,1-1.8c0.3-0.6,0.6-1.3,0.8-1.9L47.8,1.1z M53.5,13.2v4h-2.2V5.3 | |||
| h-0.7c-0.4,0.7-0.7,1.3-1.1,1.9s-0.8,1.1-1.2,1.6c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.3-0.3-0.4-0.4S47.2,7.8,47,7.7 | |||
| c-0.2-0.1-0.3-0.2-0.4-0.3C47,6.9,47.4,6.5,47.8,6c0.4-0.5,0.7-1.1,1-1.7s0.6-1.2,0.9-1.8c0.3-0.6,0.5-1.3,0.7-1.9l2.1,0.5 | |||
| c-0.1,0.4-0.3,0.8-0.4,1.1s-0.3,0.7-0.5,1.1h7v2h-5.1v1.9h4.7v1.9h-4.7v2h4.9v2H53.5z"/> | |||
| <path class="st0" d="M69.3,11.3v6h-2.2v-6h-7V9.1h7V3.6H61V1.5h14.4v2.1h-6v5.5h7.1v2.2H69.3z M64.1,4.2c0.1,0.3,0.3,0.6,0.4,0.9 | |||
| s0.3,0.6,0.4,1c0.1,0.3,0.2,0.6,0.4,0.9c0.1,0.3,0.2,0.6,0.2,0.8l-2.1,0.6c0-0.2-0.1-0.5-0.2-0.8s-0.2-0.6-0.3-1 | |||
| c-0.1-0.3-0.2-0.7-0.4-1c-0.1-0.3-0.3-0.7-0.4-1L64.1,4.2z M74.6,4.7c-0.3,0.7-0.7,1.4-1,2.1c-0.3,0.7-0.7,1.2-1,1.7l-1.9-0.5 | |||
| c0.1-0.3,0.3-0.6,0.4-0.9s0.3-0.6,0.4-1c0.1-0.3,0.3-0.7,0.4-1c0.1-0.3,0.2-0.6,0.3-0.9L74.6,4.7z"/> | |||
| <path class="st0" d="M89.4,2c0.4,0.4,0.9,0.8,1.3,1.3c0.5,0.5,0.9,0.9,1.3,1.4s0.8,0.9,1.2,1.4c0.4,0.5,0.7,0.9,0.9,1.3l-1.8,1.3 | |||
| c-0.1-0.2-0.3-0.4-0.4-0.6c-0.2-0.2-0.3-0.5-0.5-0.7c-1.6,0.1-3,0.1-4.1,0.2c-1.2,0.1-2.1,0.1-3,0.2c-0.8,0-1.5,0.1-2,0.1 | |||
| s-1,0.1-1.3,0.1s-0.6,0.1-0.8,0.1c-0.2,0-0.4,0.1-0.5,0.1c0-0.1-0.1-0.2-0.1-0.4c-0.1-0.2-0.1-0.4-0.2-0.6 | |||
| c-0.1-0.2-0.1-0.4-0.2-0.6C79,6.3,78.9,6.2,78.9,6c0.3-0.1,0.6-0.2,0.9-0.5c0.3-0.3,0.7-0.6,1-1c0.4-0.4,0.7-0.8,1.1-1.2 | |||
| c0.4-0.4,0.7-0.9,1-1.3c0.3-0.4,0.6-0.8,0.8-1.2c0.2-0.4,0.4-0.6,0.5-0.8L86.4,1c-0.6,0.8-1.2,1.7-1.9,2.4s-1.4,1.5-2,2.2l7.4-0.2 | |||
| c-0.3-0.4-0.7-0.8-1.1-1.2c-0.4-0.4-0.7-0.7-1-1.1L89.4,2z M82.4,16.3v0.9h-2.2V9.3h12.2v7.9H90v-0.9H82.4z M82.4,14.2H90v-2.9 | |||
| h-7.6V14.2z"/> | |||
| </g> | |||
| </svg> | |||
| @@ -0,0 +1 @@ | |||
| <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1636355832057" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8359" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M1024 767.6928c0 7.168-2.8672 12.0832-8.4992 14.9504L571.2896 1021.8496 569.1392 1021.8496C567.7056 1023.2832 565.5552 1024 562.688 1024c-2.8672 0-5.0176-0.7168-6.4512-2.1504L554.1888 1021.8496 109.9776 782.6432C104.2432 779.776 101.376 774.8608 101.376 767.6928L101.376 255.1808 101.376 253.0304c0-1.3312 0.7168-2.8672 2.1504-4.3008L103.5264 246.5792l4.3008-4.3008 2.1504-2.1504L554.1888 1.024c5.7344-1.3312 11.3664-1.3312 17.1008 0l444.2112 239.2064 2.1504 2.1504 4.3008 4.3008 0 2.1504L1024 253.0304l0 2.1504L1024 767.6928zM135.5776 757.0432l410.0096 222.1056 0-474.112L135.5776 282.9312 135.5776 757.0432zM154.8288 255.1808 562.688 475.136l407.8592-219.9552L562.688 35.2256 154.8288 255.1808zM989.7984 757.0432l0-474.112L579.7888 505.0368l0 474.112L989.7984 757.0432z" p-id="8360"></path></svg> | |||
| @@ -0,0 +1 @@ | |||
| <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1637739132178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1729" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M512 69.717333l383.018667 221.141334v442.282666L512 954.282667 128.981333 733.141333V290.858667L512 69.717333zM192.96 375.402667v320.768L480 861.888V541.141333l-287.04-165.76z m638.058667 0L544 541.162667V861.866667l287.018667-165.717334V375.424zM512 143.637333L215.722667 314.666667 512 485.717333l296.256-171.050666L512 143.616z" fill="#8a8a8a" p-id="1730"></path></svg> | |||
| @@ -523,6 +523,23 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| Get(notify.GetThread). | |||
| Patch(notify.ReadThread) | |||
| }, reqToken()) | |||
| adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) | |||
| //Project board | |||
| m.Group("/projectboard", func() { | |||
| m.Get("/restoreFork", adminReq, repo.RestoreForkNumber) | |||
| m.Get("/downloadAll", adminReq, repo.ServeAllProjectsPeriodStatisticsFile) | |||
| m.Get("/downloadAllOpenI", adminReq, repo.ServeAllProjectsOpenIStatisticsFile) | |||
| m.Group("/project", func() { | |||
| m.Get("", adminReq, repo.GetAllProjectsPeriodStatistics) | |||
| m.Group("/:id", func() { | |||
| m.Get("", adminReq, repo.GetProjectLatestStatistics) | |||
| m.Get("/period", adminReq, repo.GetProjectPeriodStatistics) | |||
| }) | |||
| }) | |||
| }) | |||
| // Users | |||
| m.Group("/users", func() { | |||
| @@ -858,8 +875,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| }) | |||
| m.Group("/train-job", func() { | |||
| m.Group("/:jobid", func() { | |||
| m.Get("", repo.GetModelArtsTrainJob) | |||
| m.Get("", repo.GetModelArtsTrainJobVersion) | |||
| m.Get("/log", repo.TrainJobGetLog) | |||
| m.Post("/del_version", repo.DelTrainJobVersion) | |||
| m.Post("/stop_version", repo.StopTrainJobVersion) | |||
| m.Get("/model_list", repo.ModelList) | |||
| }) | |||
| }) | |||
| }, reqRepoReader(models.UnitTypeCloudBrain)) | |||
| @@ -6,12 +6,15 @@ | |||
| package repo | |||
| import ( | |||
| "net/http" | |||
| "strconv" | |||
| "strings" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/modelarts" | |||
| "net/http" | |||
| "strconv" | |||
| "code.gitea.io/gitea/modules/storage" | |||
| ) | |||
| func GetModelArtsNotebook(ctx *context.APIContext) { | |||
| @@ -72,56 +75,247 @@ func GetModelArtsTrainJob(ctx *context.APIContext) { | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "JobID": jobID, | |||
| "JobStatus": job.Status, | |||
| "JobDuration": job.Duration, | |||
| "JobID": jobID, | |||
| "JobStatus": job.Status, | |||
| "JobDuration": job.Duration, | |||
| }) | |||
| } | |||
| func TrainJobGetLog(ctx *context.APIContext) { | |||
| func GetModelArtsTrainJobVersion(ctx *context.APIContext) { | |||
| var ( | |||
| err error | |||
| ) | |||
| log.Info("test") | |||
| jobID := ctx.Params(":jobid") | |||
| versionName := ctx.Query("version_name") | |||
| job, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) | |||
| if err != nil { | |||
| ctx.NotFound(err) | |||
| return | |||
| } | |||
| result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10)) | |||
| if err != nil { | |||
| ctx.NotFound(err) | |||
| return | |||
| } | |||
| job.Status = modelarts.TransTrainJobStatus(result.IntStatus) | |||
| job.Duration = result.Duration | |||
| job.TrainJobDuration = result.TrainJobDuration | |||
| if result.Duration != 0 { | |||
| job.TrainJobDuration = addZero(result.Duration/3600000) + ":" + addZero(result.Duration%3600000/60000) + ":" + addZero(result.Duration%60000/1000) | |||
| } else { | |||
| job.TrainJobDuration = "00:00:00" | |||
| } | |||
| err = models.UpdateTrainJobVersion(job) | |||
| if err != nil { | |||
| log.Error("UpdateJob failed:", err) | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "JobID": jobID, | |||
| "JobStatus": job.Status, | |||
| "JobDuration": job.TrainJobDuration, | |||
| }) | |||
| } | |||
| func addZero(t int64) (m string) { | |||
| if t < 10 { | |||
| m = "0" + strconv.FormatInt(t, 10) | |||
| return m | |||
| } else { | |||
| return strconv.FormatInt(t, 10) | |||
| } | |||
| } | |||
| func TrainJobGetLog(ctx *context.APIContext) { | |||
| var ( | |||
| err error | |||
| ) | |||
| var jobID = ctx.Params(":jobid") | |||
| var logFileName = ctx.Query("file_name") | |||
| var versionName = ctx.Query("version_name") | |||
| // var logFileName = ctx.Query("file_name") | |||
| var baseLine = ctx.Query("base_line") | |||
| var order = ctx.Query("order") | |||
| var lines = ctx.Query("lines") | |||
| lines_int, err := strconv.Atoi(lines) | |||
| if err != nil { | |||
| log.Error("change lines(%d) string to int failed", lines_int) | |||
| } | |||
| if order != modelarts.OrderDesc && order != modelarts.OrderAsc { | |||
| log.Error("order(%s) check failed", order) | |||
| ctx.JSON(http.StatusBadRequest, map[string]interface{}{ | |||
| "err_msg": "order check failed", | |||
| "err_msg": "order check failed", | |||
| }) | |||
| return | |||
| } | |||
| task, err := models.GetCloudbrainByJobID(jobID) | |||
| resultLogFile, result, err := trainJobGetLogContent(jobID, versionName, baseLine, order, lines_int) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||
| ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ | |||
| "err_msg": "GetCloudbrainByJobID failed", | |||
| }) | |||
| log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error()) | |||
| // ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||
| return | |||
| } | |||
| result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, logFileName, order, modelarts.Lines) | |||
| ctx.Data["log_file_name"] = resultLogFile.LogFileList[0] | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "JobID": jobID, | |||
| "LogFileName": resultLogFile.LogFileList[0], | |||
| "StartLine": result.StartLine, | |||
| "EndLine": result.EndLine, | |||
| "Content": result.Content, | |||
| "Lines": result.Lines, | |||
| }) | |||
| } | |||
| func trainJobGetLogContent(jobID string, versionName string, baseLine string, order string, lines int) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) { | |||
| task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||
| return nil, nil, err | |||
| } | |||
| resultLogFile, err := modelarts.GetTrainJobLogFileNames(jobID, strconv.FormatInt(task.VersionID, 10)) | |||
| if err != nil { | |||
| log.Error("GetTrainJobLogFileNames(%s) failed:%v", jobID, err.Error()) | |||
| return nil, nil, err | |||
| } | |||
| result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, resultLogFile.LogFileList[0], order, lines) | |||
| if err != nil { | |||
| log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) | |||
| ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ | |||
| "err_msg": "GetTrainJobLog failed", | |||
| }) | |||
| return nil, nil, err | |||
| } | |||
| return resultLogFile, result, err | |||
| } | |||
| func DelTrainJobVersion(ctx *context.APIContext) { | |||
| var ( | |||
| err error | |||
| ) | |||
| var jobID = ctx.Params(":jobid") | |||
| var versionName = ctx.Query("version_name") | |||
| task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||
| ctx.NotFound(err) | |||
| return | |||
| } | |||
| //删除modelarts上的记录 | |||
| _, err = modelarts.DelTrainJobVersion(jobID, strconv.FormatInt(task.VersionID, 10)) | |||
| if err != nil { | |||
| log.Error("DelTrainJobVersion(%s) failed:%v", task.JobName, err.Error()) | |||
| ctx.NotFound(err) | |||
| return | |||
| } | |||
| //删除数据库记录 | |||
| err = models.DeleteJob(task) | |||
| if err != nil { | |||
| ctx.ServerError("DeleteJob failed", err) | |||
| ctx.NotFound(err) | |||
| return | |||
| } | |||
| //获取删除后的版本数量 | |||
| repo := ctx.Repo.Repository | |||
| VersionTaskList, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{ | |||
| RepoID: repo.ID, | |||
| Type: models.TypeCloudBrainTwo, | |||
| JobType: string(models.JobTypeTrain), | |||
| JobID: jobID, | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("get VersionListCount faild", err) | |||
| return | |||
| } | |||
| // 判断当前删掉的任务是否是最新版本,若是,将排序后的TotalVersionCount置为删掉的最新版本的TotalVersionCount,若不是,按时间排序后的版本列表的第一个版本设置为最新版本,TotalVersionCount不变 | |||
| if task.IsLatestVersion == modelarts.IsLatestVersion { | |||
| err = models.SetVersionCountAndLatestVersion(jobID, VersionTaskList[0].Cloudbrain.VersionName, VersionListCount, modelarts.IsLatestVersion, task.TotalVersionCount) | |||
| if err != nil { | |||
| ctx.ServerError("UpdateJobVersionCount failed", err) | |||
| return | |||
| } | |||
| } else { | |||
| err = models.SetVersionCountAndLatestVersion(jobID, VersionTaskList[0].VersionName, VersionListCount, modelarts.IsLatestVersion, VersionTaskList[0].Cloudbrain.TotalVersionCount) | |||
| if err != nil { | |||
| ctx.ServerError("UpdateJobVersionCount failed", err) | |||
| return | |||
| } | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "JobID": jobID, | |||
| "VersionName": versionName, | |||
| "StatusOK": 0, | |||
| }) | |||
| } | |||
| func StopTrainJobVersion(ctx *context.APIContext) { | |||
| var ( | |||
| err error | |||
| ) | |||
| var jobID = ctx.Params(":jobid") | |||
| var versionName = ctx.Query("version_name") | |||
| task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||
| return | |||
| } | |||
| _, err = modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10)) | |||
| if err != nil { | |||
| log.Error("StopTrainJob(%s) failed:%v", task.JobName, err.Error()) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "JobID": jobID, | |||
| "VersionName": versionName, | |||
| "StatusOK": 0, | |||
| }) | |||
| } | |||
| func ModelList(ctx *context.APIContext) { | |||
| var ( | |||
| err error | |||
| ) | |||
| var jobID = ctx.Params(":jobid") | |||
| var versionName = ctx.Query("version_name") | |||
| parentDir := ctx.Query("parentDir") | |||
| dirArray := strings.Split(parentDir, "/") | |||
| task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||
| return | |||
| } | |||
| models, err := storage.GetObsListObject(task.JobName, parentDir, versionName) | |||
| if err != nil { | |||
| log.Info("get TrainJobListModel failed:", err) | |||
| ctx.ServerError("GetObsListObject:", err) | |||
| return | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "JobID": jobID, | |||
| "StartLine": result.StartLine, | |||
| "EndLine": result.EndLine, | |||
| "Content": result.Content, | |||
| "Lines": result.Lines, | |||
| "JobID": jobID, | |||
| "VersionName": versionName, | |||
| "StatusOK": 0, | |||
| "Path": dirArray, | |||
| "Dirs": models, | |||
| "task": task, | |||
| "PageIsCloudBrain": true, | |||
| }) | |||
| } | |||
| @@ -0,0 +1,641 @@ | |||
| package repo | |||
| import ( | |||
| "fmt" | |||
| "net/http" | |||
| "net/url" | |||
| "strconv" | |||
| "time" | |||
| "github.com/360EntSecGroup-Skylar/excelize/v2" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| ) | |||
| const DEFAULT_PAGE_SIZE = 10 | |||
| const DATE_FORMAT = "2006-01-02" | |||
| const EXCEL_DATE_FORMAT = "20060102" | |||
| type ProjectsPeriodData struct { | |||
| RecordBeginTime string `json:"recordBeginTime"` | |||
| LastUpdatedTime string `json:"lastUpdatedTime"` | |||
| PageSize int `json:"pageSize"` | |||
| TotalPage int `json:"totalPage"` | |||
| TotalCount int64 `json:"totalCount"` | |||
| PageRecords []*models.RepoStatistic `json:"pageRecords"` | |||
| } | |||
| type UserInfo struct { | |||
| User string `json:"user"` | |||
| Mode int `json:"mode"` | |||
| PR int64 `json:"pr"` | |||
| Commit int `json:"commit"` | |||
| RelAvatarLink string `json:"relAvatarLink"` | |||
| Email string `json:"email"` | |||
| } | |||
| type ProjectLatestData struct { | |||
| RecordBeginTime string `json:"recordBeginTime"` | |||
| LastUpdatedTime string `json:"lastUpdatedTime"` | |||
| CreatTime string `json:"creatTime"` | |||
| OpenI float64 `json:"openi"` | |||
| Comment int64 `json:"comment"` | |||
| View int64 `json:"view"` | |||
| Download int64 `json:"download"` | |||
| IssueClosedRatio float32 `json:"issueClosedRatio"` | |||
| Impact float64 `json:"impact"` | |||
| Completeness float64 `json:"completeness"` | |||
| Liveness float64 `json:"liveness"` | |||
| ProjectHealth float64 `json:"projectHealth"` | |||
| TeamHealth float64 `json:"teamHealth"` | |||
| Growth float64 `json:"growth"` | |||
| Description string `json:"description"` | |||
| Top10 []UserInfo `json:"top10"` | |||
| } | |||
| func RestoreForkNumber(ctx *context.Context) { | |||
| repos, err := models.GetAllRepositories() | |||
| if err != nil { | |||
| log.Error("GetAllRepositories failed: %v", err.Error()) | |||
| return | |||
| } | |||
| for _, repo := range repos { | |||
| models.RestoreRepoStatFork(int64(repo.NumForks), repo.ID) | |||
| } | |||
| ctx.JSON(http.StatusOK, struct{}{}) | |||
| } | |||
| func GetAllProjectsPeriodStatistics(ctx *context.Context) { | |||
| recordBeginTime, err := getRecordBeginTime() | |||
| if err != nil { | |||
| log.Error("Can not get record begin time", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
| return | |||
| } | |||
| beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime) | |||
| if err != nil { | |||
| log.Error("Parameter is wrong", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.parameter_is_wrong")) | |||
| return | |||
| } | |||
| q := ctx.QueryTrim("q") | |||
| page := ctx.QueryInt("page") | |||
| if page <= 0 { | |||
| page = 1 | |||
| } | |||
| pageSize := ctx.QueryInt("pagesize") | |||
| if pageSize <= 0 { | |||
| pageSize = DEFAULT_PAGE_SIZE | |||
| } | |||
| orderBy := getOrderBy(ctx) | |||
| latestUpdatedTime, latestDate, err := models.GetRepoStatLastUpdatedTime() | |||
| if err != nil { | |||
| log.Error("Can not query the last updated time.", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error")) | |||
| return | |||
| } | |||
| countSql := generateCountSql(beginTime, endTime, latestDate, q) | |||
| total, err := models.CountRepoStatByRawSql(countSql) | |||
| if err != nil { | |||
| log.Error("Can not query total count.", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) | |||
| return | |||
| } | |||
| sql := generateSqlByType(ctx, beginTime, endTime, latestDate, q, orderBy, page, pageSize) | |||
| projectsPeriodData := ProjectsPeriodData{ | |||
| RecordBeginTime: recordBeginTime.Format(DATE_FORMAT), | |||
| PageSize: pageSize, | |||
| TotalPage: getTotalPage(total, pageSize), | |||
| TotalCount: total, | |||
| LastUpdatedTime: latestUpdatedTime, | |||
| PageRecords: models.GetRepoStatisticByRawSql(sql), | |||
| } | |||
| ctx.JSON(http.StatusOK, projectsPeriodData) | |||
| } | |||
| func generateSqlByType(ctx *context.Context, beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
| sql := "" | |||
| if ctx.QueryTrim("type") == "all" { | |||
| sql = generateTypeAllSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize) | |||
| } else { | |||
| sql = generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize) | |||
| } | |||
| return sql | |||
| } | |||
| func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | |||
| recordBeginTime, err := getRecordBeginTime() | |||
| if err != nil { | |||
| log.Error("Can not get record begin time", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
| return | |||
| } | |||
| beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime) | |||
| if err != nil { | |||
| log.Error("Parameter is wrong", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.parameter_is_wrong")) | |||
| return | |||
| } | |||
| q := ctx.QueryTrim("q") | |||
| page := ctx.QueryInt("page") | |||
| if page <= 0 { | |||
| page = 1 | |||
| } | |||
| pageSize := 1000 | |||
| orderBy := getOrderBy(ctx) | |||
| _, latestDate, err := models.GetRepoStatLastUpdatedTime() | |||
| if err != nil { | |||
| log.Error("Can not query the last updated time.", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error")) | |||
| return | |||
| } | |||
| countSql := generateCountSql(beginTime, endTime, latestDate, q) | |||
| total, err := models.CountRepoStatByRawSql(countSql) | |||
| if err != nil { | |||
| log.Error("Can not query total count.", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) | |||
| return | |||
| } | |||
| var projectAnalysis = ctx.Tr("repo.repo_stat_inspect") | |||
| fileName := getFileName(ctx, beginTime, endTime, projectAnalysis) | |||
| totalPage := getTotalPage(total, pageSize) | |||
| f := excelize.NewFile() | |||
| index := f.NewSheet(projectAnalysis) | |||
| f.DeleteSheet("Sheet1") | |||
| for k, v := range allProjectsPeroidHeader(ctx) { | |||
| f.SetCellValue(projectAnalysis, k, v) | |||
| } | |||
| var row = 2 | |||
| for i := 0; i <= totalPage; i++ { | |||
| pageRecords := models.GetRepoStatisticByRawSql(generateSqlByType(ctx, beginTime, endTime, latestDate, q, orderBy, i+1, pageSize)) | |||
| for _, record := range pageRecords { | |||
| for k, v := range allProjectsPeroidValues(row, record, ctx) { | |||
| f.SetCellValue(projectAnalysis, k, v) | |||
| } | |||
| row++ | |||
| } | |||
| } | |||
| f.SetActiveSheet(index) | |||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(fileName)) | |||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
| f.WriteTo(ctx.Resp) | |||
| } | |||
| func ServeAllProjectsOpenIStatisticsFile(ctx *context.Context) { | |||
| page := ctx.QueryInt("page") | |||
| if page <= 0 { | |||
| page = 1 | |||
| } | |||
| pageSize := 1000 | |||
| _, latestDate, err := models.GetRepoStatLastUpdatedTime() | |||
| if err != nil { | |||
| log.Error("Can not query the last updated time.", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error")) | |||
| return | |||
| } | |||
| date := ctx.QueryTrim("date") | |||
| if date == "" { | |||
| date = latestDate | |||
| } | |||
| countSql := generateOpenICountSql(date) | |||
| total, err := models.CountRepoStatByRawSql(countSql) | |||
| if err != nil { | |||
| log.Error("Can not query total count.", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) | |||
| return | |||
| } | |||
| var projectAnalysis = ctx.Tr("repo.repo_stat_inspect") | |||
| fileName := "项目分析_OPENI_" + date + ".xlsx" | |||
| totalPage := getTotalPage(total, pageSize) | |||
| f := excelize.NewFile() | |||
| index := f.NewSheet(projectAnalysis) | |||
| f.DeleteSheet("Sheet1") | |||
| for k, v := range allProjectsOpenIHeader() { | |||
| f.SetCellValue(projectAnalysis, k, v) | |||
| } | |||
| var row = 2 | |||
| for i := 0; i <= totalPage; i++ { | |||
| pageRecords := models.GetRepoStatisticByRawSql(generateTypeAllOpenISql(date, i+1, pageSize)) | |||
| for _, record := range pageRecords { | |||
| for k, v := range allProjectsOpenIValues(row, record, ctx) { | |||
| f.SetCellValue(projectAnalysis, k, v) | |||
| } | |||
| row++ | |||
| } | |||
| } | |||
| f.SetActiveSheet(index) | |||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(fileName)) | |||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
| f.WriteTo(ctx.Resp) | |||
| } | |||
| func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, projectAnalysis string) string { | |||
| baseName := projectAnalysis + "_" | |||
| if ctx.QueryTrim("q") != "" { | |||
| baseName = baseName + ctx.QueryTrim("q") + "_" | |||
| } | |||
| if ctx.QueryTrim("type") == "all" { | |||
| baseName = baseName + ctx.Tr("repo.all") | |||
| } else { | |||
| baseName = baseName + beginTime.AddDate(0, 0, -1).Format(EXCEL_DATE_FORMAT) + "_" + endTime.AddDate(0, 0, -1).Format(EXCEL_DATE_FORMAT) | |||
| } | |||
| frontName := baseName + ".xlsx" | |||
| return frontName | |||
| } | |||
| func allProjectsPeroidHeader(ctx *context.Context) map[string]string { | |||
| return map[string]string{"A1": ctx.Tr("admin.repos.id"), "B1": ctx.Tr("admin.repos.projectName"), "C1": ctx.Tr("repo.owner"), "D1": ctx.Tr("admin.repos.isPrivate"), "E1": ctx.Tr("admin.repos.openi"), "F1": ctx.Tr("admin.repos.visit"), "G1": ctx.Tr("admin.repos.download"), "H1": ctx.Tr("admin.repos.pr"), "I1": ctx.Tr("admin.repos.commit"), | |||
| "J1": ctx.Tr("admin.repos.watches"), "K1": ctx.Tr("admin.repos.stars"), "L1": ctx.Tr("admin.repos.forks"), "M1": ctx.Tr("admin.repos.issues"), "N1": ctx.Tr("admin.repos.closedIssues"), "O1": ctx.Tr("admin.repos.contributor")} | |||
| } | |||
| func allProjectsPeroidValues(row int, rs *models.RepoStatistic, ctx *context.Context) map[string]string { | |||
| return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.Name, getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
| getCellName("F", row): strconv.FormatInt(rs.NumVisits, 10), getCellName("G", row): strconv.FormatInt(rs.NumDownloads, 10), getCellName("H", row): strconv.FormatInt(rs.NumPulls, 10), getCellName("I", row): strconv.FormatInt(rs.NumCommits, 10), | |||
| getCellName("J", row): strconv.FormatInt(rs.NumWatches, 10), getCellName("K", row): strconv.FormatInt(rs.NumStars, 10), getCellName("L", row): strconv.FormatInt(rs.NumForks, 10), getCellName("M", row): strconv.FormatInt(rs.NumIssues, 10), | |||
| getCellName("N", row): strconv.FormatInt(rs.NumClosedIssues, 10), getCellName("O", row): strconv.FormatInt(rs.NumContributor, 10), | |||
| } | |||
| } | |||
| func allProjectsOpenIHeader() map[string]string { | |||
| return map[string]string{"A1": "ID", "B1": "项目名称", "C1": "拥有者", "D1": "是否私有", "E1": "OpenI指数", | |||
| "F1": "影响力", "G1": "成熟度", "H1": "活跃度", "I1": "项目健康度", "J1": "团队健康度", "K1": "项目发展趋势", | |||
| "L1": "关注数", "M1": "点赞数", "N1": "派生数", "O1": "代码下载量", "P1": "评论数", "Q1": "浏览量", "R1": "已解决任务数", "S1": "版本发布数量", "T1": "有效开发年龄", | |||
| "U1": "数据集", "V1": "模型数", "W1": "百科页面数量", "X1": "提交数", "Y1": "任务数", "Z1": "PR数", "AA1": "版本发布数量", "AB1": "任务完成比例", "AC1": "贡献者数", "AD1": "关键贡献者数", | |||
| "AE1": "新人增长量", "AF1": "代码规模增长量", "AG1": "任务增长量", "AH1": "新人增长量", "AI1": "提交增长量", "AJ1": "评论增长量", | |||
| } | |||
| } | |||
| func allProjectsOpenIValues(row int, rs *models.RepoStatistic, ctx *context.Context) map[string]string { | |||
| return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.Name, getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
| getCellName("F", row): strconv.FormatFloat(rs.Impact, 'f', 2, 64), getCellName("G", row): strconv.FormatFloat(rs.Completeness, 'f', 2, 64), getCellName("H", row): strconv.FormatFloat(rs.Liveness, 'f', 2, 64), getCellName("I", row): strconv.FormatFloat(rs.ProjectHealth, 'f', 2, 64), getCellName("J", row): strconv.FormatFloat(rs.TeamHealth, 'f', 2, 64), getCellName("K", row): strconv.FormatFloat(rs.Growth, 'f', 2, 64), | |||
| getCellName("L", row): strconv.FormatInt(rs.NumWatches, 10), getCellName("M", row): strconv.FormatInt(rs.NumStars, 10), getCellName("N", row): strconv.FormatInt(rs.NumForks, 10), getCellName("O", row): strconv.FormatInt(rs.NumDownloads, 10), | |||
| getCellName("P", row): strconv.FormatInt(rs.NumComments, 10), getCellName("Q", row): strconv.FormatInt(rs.NumVisits, 10), getCellName("R", row): strconv.FormatInt(rs.NumClosedIssues, 10), getCellName("S", row): strconv.FormatInt(rs.NumVersions, 10), | |||
| getCellName("T", row): strconv.FormatInt(rs.NumDevMonths, 10), getCellName("U", row): strconv.FormatInt(rs.DatasetSize, 10), getCellName("V", row): strconv.FormatInt(rs.NumModels, 10), getCellName("W", row): strconv.FormatInt(rs.NumWikiViews, 10), | |||
| getCellName("X", row): strconv.FormatInt(rs.NumCommits, 10), getCellName("Y", row): strconv.FormatInt(rs.NumIssues, 10), getCellName("Z", row): strconv.FormatInt(rs.NumPulls, 10), getCellName("AA", row): strconv.FormatInt(rs.NumVersions, 10), | |||
| getCellName("AB", row): strconv.FormatFloat(float64(rs.IssueFixedRate), 'f', 2, 64), getCellName("AC", row): strconv.FormatInt(rs.NumContributor, 10), getCellName("AD", row): strconv.FormatInt(rs.NumKeyContributor, 10), getCellName("AE", row): strconv.FormatInt(rs.NumContributorsGrowth, 10), | |||
| getCellName("AF", row): strconv.FormatInt(rs.NumCommitLinesGrowth, 10), getCellName("AG", row): strconv.FormatInt(rs.NumIssuesGrowth, 10), getCellName("AH", row): strconv.FormatInt(rs.NumContributorsGrowth, 10), getCellName("AI", row): strconv.FormatInt(rs.NumCommitsGrowth, 10), getCellName("AJ", row): strconv.FormatInt(rs.NumCommentsGrowth, 10), | |||
| } | |||
| } | |||
| func getCellName(col string, row int) string { | |||
| return col + strconv.Itoa(row) | |||
| } | |||
| func getIsPrivateDisplay(private bool, ctx *context.Context) string { | |||
| if private { | |||
| return ctx.Tr("admin.repos.yes") | |||
| } else { | |||
| return ctx.Tr("admin.repos.no") | |||
| } | |||
| } | |||
| func GetProjectLatestStatistics(ctx *context.Context) { | |||
| repoId := ctx.Params(":id") | |||
| recordBeginTime, err := getRecordBeginTime() | |||
| if err != nil { | |||
| log.Error("Can not get record begin time", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
| return | |||
| } | |||
| latestUpdatedTime, latestDate, err := models.GetRepoStatLastUpdatedTime(repoId) | |||
| repoIdInt, _ := strconv.ParseInt(repoId, 10, 64) | |||
| repoStat, err := models.GetRepoStatisticByDateAndRepoId(latestDate, repoIdInt) | |||
| if err != nil { | |||
| log.Error("Can not get the repo statistics "+repoId, err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.get_repo_stat_error")) | |||
| return | |||
| } | |||
| repository, err := models.GetRepositoryByID(repoIdInt) | |||
| if err != nil { | |||
| log.Error("Can not get the repo info "+repoId, err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.get_repo_info_error")) | |||
| return | |||
| } | |||
| projectLatestData := ProjectLatestData{ | |||
| RecordBeginTime: recordBeginTime.Format(DATE_FORMAT), | |||
| CreatTime: time.Unix(int64(repository.CreatedUnix), 0).Format(DATE_FORMAT), | |||
| LastUpdatedTime: latestUpdatedTime, | |||
| OpenI: repoStat.RadarTotal, | |||
| Comment: repoStat.NumComments, | |||
| View: repoStat.NumVisits, | |||
| Download: repoStat.NumDownloads, | |||
| IssueClosedRatio: repoStat.IssueFixedRate, | |||
| Impact: repoStat.Impact, | |||
| Completeness: repoStat.Completeness, | |||
| Liveness: repoStat.Liveness, | |||
| ProjectHealth: repoStat.ProjectHealth, | |||
| TeamHealth: repoStat.TeamHealth, | |||
| Growth: repoStat.Growth, | |||
| Description: repository.Description, | |||
| } | |||
| contributors, err := models.GetTop10Contributor(repository.RepoPath()) | |||
| if err != nil { | |||
| log.Error("can not get contributors", err) | |||
| } | |||
| users := make([]UserInfo, 0) | |||
| for _, contributor := range contributors { | |||
| mode := repository.GetCollaboratorMode(contributor.UserId) | |||
| if mode == -1 { | |||
| if contributor.IsAdmin { | |||
| mode = int(models.AccessModeAdmin) | |||
| } | |||
| if contributor.UserId == repository.OwnerID { | |||
| mode = int(models.AccessModeOwner) | |||
| } | |||
| } | |||
| pr := models.GetPullCountByUserAndRepoId(repoIdInt, contributor.UserId) | |||
| userInfo := UserInfo{ | |||
| User: contributor.Committer, | |||
| Commit: contributor.CommitCnt, | |||
| Mode: mode, | |||
| PR: pr, | |||
| RelAvatarLink: contributor.RelAvatarLink, | |||
| Email: contributor.Email, | |||
| } | |||
| users = append(users, userInfo) | |||
| } | |||
| projectLatestData.Top10 = users | |||
| ctx.JSON(http.StatusOK, projectLatestData) | |||
| } | |||
| func GetProjectPeriodStatistics(ctx *context.Context) { | |||
| repoId := ctx.Params(":id") | |||
| recordBeginTime, err := getRecordBeginTime() | |||
| if err != nil { | |||
| log.Error("Can not get record begin time", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
| return | |||
| } | |||
| repoIdInt, _ := strconv.ParseInt(repoId, 10, 64) | |||
| if err != nil { | |||
| log.Error("Can not get record begin time", err) | |||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) | |||
| return | |||
| } | |||
| beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime) | |||
| isOpenI := ctx.QueryBool("openi") | |||
| var repositorys []*models.RepoStatistic | |||
| if isOpenI { | |||
| repositorys = models.GetRepoStatisticByRawSql(generateRadarSql(beginTime, endTime, repoIdInt)) | |||
| } else { | |||
| repositorys = models.GetRepoStatisticByRawSql(generateTargetSql(beginTime, endTime, repoIdInt)) | |||
| } | |||
| ctx.JSON(http.StatusOK, repositorys) | |||
| } | |||
| func generateRadarSql(beginTime time.Time, endTime time.Time, repoId int64) string { | |||
| sql := "SELECT date, impact, completeness, liveness, project_health, team_health, growth, radar_total FROM repo_statistic" + | |||
| " where repo_id=" + strconv.FormatInt(repoId, 10) + " and created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " order by created_unix" | |||
| return sql | |||
| } | |||
| func generateTargetSql(beginTime time.Time, endTime time.Time, repoId int64) string { | |||
| sql := "SELECT date, num_visits,num_downloads_added as num_downloads,num_commits_added as num_commits FROM repo_statistic" + | |||
| " where repo_id=" + strconv.FormatInt(repoId, 10) + " and created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " order by created_unix desc" | |||
| return sql | |||
| } | |||
| func generateCountSql(beginTime time.Time, endTime time.Time, latestDate string, q string) string { | |||
| countSql := "SELECT count(*) FROM " + | |||
| "(SELECT repo_id FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||
| "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||
| " where A.repo_id=B.repo_id" | |||
| if q != "" { | |||
| countSql = countSql + " and B.name like '%" + q + "%'" | |||
| } | |||
| return countSql | |||
| } | |||
| func generateOpenICountSql(latestDate string) string { | |||
| countSql := "SELECT count(*) FROM " + | |||
| "public.repo_statistic where date='" + latestDate + "'" | |||
| return countSql | |||
| } | |||
| func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
| sql := "SELECT A.repo_id,name,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
| "(SELECT repo_id,sum(num_visits) as num_visits " + | |||
| " FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||
| "(SELECT repo_id,name,owner_name,is_private,radar_total,num_watches,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor from public.repo_statistic where date='" + latestDate + "') B" + | |||
| " where A.repo_id=B.repo_id" | |||
| if q != "" { | |||
| sql = sql + " and name like '%" + q + "%'" | |||
| } | |||
| sql = sql + " order by " + orderBy + " desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
| return sql | |||
| } | |||
| func generateTypeAllOpenISql(latestDate string, page int, pageSize int) string { | |||
| sql := "SELECT id, repo_id, date, num_watches, num_stars, num_forks, num_downloads, num_comments, num_visits, num_closed_issues, num_versions, num_dev_months, repo_size, dataset_size, num_models, num_wiki_views, num_commits, num_issues, num_pulls, issue_fixed_rate, num_contributor, num_key_contributor, num_contributors_growth, num_commits_growth, num_commit_lines_growth, num_issues_growth, num_comments_growth, impact, completeness, liveness, project_health, team_health, growth, radar_total, name, is_private, owner_name FROM " + | |||
| " public.repo_statistic where date='" + latestDate + "'" | |||
| sql = sql + " order by radar_total desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
| return sql | |||
| } | |||
| func generatePageSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
| sql := "SELECT A.repo_id,name,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
| "(SELECT repo_id,sum(num_watches_added) as num_watches,sum(num_visits) as num_visits, sum(num_downloads_added) as num_downloads,sum(num_pulls_added) as num_pulls,sum(num_commits_added) as num_commits,sum(num_stars_added) as num_stars,sum(num_forks_added) num_forks,sum(num_issues_added) as num_issues,sum(num_closed_issues_added) as num_closed_issues,sum(num_contributor_added) as num_contributor " + | |||
| " FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||
| "(SELECT repo_id,name,owner_name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||
| " where A.repo_id=B.repo_id" | |||
| if q != "" { | |||
| sql = sql + " and B.name like '%" + q + "%'" | |||
| } | |||
| sql = sql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
| return sql | |||
| } | |||
| func getOrderBy(ctx *context.Context) string { | |||
| orderBy := "" | |||
| switch ctx.Query("sort") { | |||
| case "openi": | |||
| orderBy = "B.radar_total" | |||
| case "view": | |||
| orderBy = "A.num_visits" | |||
| case "download": | |||
| orderBy = "A.num_downloads" | |||
| case "pr": | |||
| orderBy = "A.num_pulls" | |||
| case "commit": | |||
| orderBy = "A.num_commits" | |||
| case "watch": | |||
| orderBy = "A.num_watches" | |||
| case "star": | |||
| orderBy = "A.num_stars" | |||
| case "fork": | |||
| orderBy = "A.num_forks" | |||
| case "issue": | |||
| orderBy = "A.num_issues" | |||
| case "issue_closed": | |||
| orderBy = "A.num_closed_issues" | |||
| case "contributor": | |||
| orderBy = "A.num_contributor" | |||
| default: | |||
| orderBy = "B.radar_total" | |||
| } | |||
| return orderBy | |||
| } | |||
| func getTimePeroid(ctx *context.Context, recordBeginTime time.Time) (time.Time, time.Time, error) { | |||
| queryType := ctx.QueryTrim("type") | |||
| now := time.Now() | |||
| recordBeginTimeTemp := recordBeginTime.AddDate(0, 0, 1) | |||
| beginTimeStr := ctx.QueryTrim("beginTime") | |||
| endTimeStr := ctx.QueryTrim("endTime") | |||
| var beginTime time.Time | |||
| var endTime time.Time | |||
| var err error | |||
| if queryType != "" { | |||
| if queryType == "all" { | |||
| beginTime = recordBeginTimeTemp | |||
| endTime = now | |||
| } else if queryType == "yesterday" { | |||
| endTime = now | |||
| beginTime = time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, now.Location()) | |||
| } else if queryType == "current_week" { | |||
| beginTime = now.AddDate(0, 0, -int(time.Now().Weekday())+2) //begin from monday | |||
| beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location()) | |||
| endTime = now | |||
| } else if queryType == "current_month" { | |||
| endTime = now | |||
| beginTime = time.Date(endTime.Year(), endTime.Month(), 2, 0, 0, 0, 0, now.Location()) | |||
| } else if queryType == "monthly" { | |||
| endTime = now | |||
| beginTime = now.AddDate(0, -1, 1) | |||
| beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location()) | |||
| } else if queryType == "current_year" { | |||
| endTime = now | |||
| beginTime = time.Date(endTime.Year(), 1, 2, 0, 0, 0, 0, now.Location()) | |||
| } else if queryType == "last_month" { | |||
| lastMonthTime := now.AddDate(0, -1, 0) | |||
| beginTime = time.Date(lastMonthTime.Year(), lastMonthTime.Month(), 2, 0, 0, 0, 0, now.Location()) | |||
| endTime = time.Date(now.Year(), now.Month(), 2, 0, 0, 0, 0, now.Location()) | |||
| } else { | |||
| return now, now, fmt.Errorf("The value of type parameter is wrong.") | |||
| } | |||
| } else { | |||
| if beginTimeStr == "" || endTimeStr == "" { | |||
| //如果查询类型和开始时间结束时间都未设置,按queryType=all处理 | |||
| beginTime = recordBeginTimeTemp | |||
| endTime = now | |||
| } else { | |||
| beginTime, err = time.ParseInLocation("2006-01-02", beginTimeStr, time.Local) | |||
| if err != nil { | |||
| return now, now, err | |||
| } | |||
| endTime, err = time.ParseInLocation("2006-01-02", endTimeStr, time.Local) | |||
| if err != nil { | |||
| return now, now, err | |||
| } | |||
| beginTime = beginTime.AddDate(0, 0, 1) | |||
| endTime = endTime.AddDate(0, 0, 1) | |||
| } | |||
| } | |||
| if beginTime.Before(recordBeginTimeTemp) { | |||
| beginTime = recordBeginTimeTemp | |||
| } | |||
| return beginTime, endTime, nil | |||
| } | |||
| func getRecordBeginTime() (time.Time, error) { | |||
| return time.ParseInLocation(DATE_FORMAT, setting.RadarMap.RecordBeginTime, time.Local) | |||
| } | |||
| func getTotalPage(total int64, pageSize int) int { | |||
| another := 0 | |||
| if int(total)%pageSize != 0 { | |||
| another = 1 | |||
| } | |||
| return int(total)/pageSize + another | |||
| } | |||
| @@ -33,8 +33,9 @@ const ( | |||
| // tplExploreOrganizations explore organizations page template | |||
| tplExploreOrganizations base.TplName = "explore/organizations" | |||
| // tplExploreCode explore code page template | |||
| tplExploreCode base.TplName = "explore/code" | |||
| tplExploreImages base.TplName = "explore/images" | |||
| tplExploreCode base.TplName = "explore/code" | |||
| tplExploreImages base.TplName = "explore/images" | |||
| tplExploreExploreDataAnalysis base.TplName = "explore/data_analysis" | |||
| ) | |||
| // Home render home page | |||
| @@ -146,7 +147,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { | |||
| ctx.Data["SortType"] = "hot" | |||
| orderBy = models.SearchOrderByHot | |||
| } | |||
| orderBy = orderBy + ",id" | |||
| //todo:support other topics | |||
| keyword := strings.Trim(ctx.Query("q"), " ") | |||
| topic := strings.Trim(ctx.Query("topic"), " ") | |||
| @@ -501,6 +502,10 @@ func ExploreImages(ctx *context.Context) { | |||
| ctx.HTML(200, tplExploreImages) | |||
| } | |||
| func ExploreDataAnalysis(ctx *context.Context) { | |||
| ctx.HTML(200, tplExploreExploreDataAnalysis) | |||
| } | |||
| // NotFound render 404 page | |||
| func NotFound(ctx *context.Context) { | |||
| ctx.Data["Title"] = "Page Not Found" | |||
| @@ -43,7 +43,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Post("/manager/restart", Restart) | |||
| m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) | |||
| m.Post("/tool/update_all_repo_commit_cnt", UpdateAllRepoCommitCnt) | |||
| m.Post("/tool/repo_stat", RepoStatisticManually) | |||
| m.Post("/tool/repo_stat/:date", RepoStatisticManually) | |||
| m.Post("/tool/update_repo_visit/:date", UpdateRepoVisit) | |||
| }, CheckInternalToken) | |||
| } | |||
| @@ -39,8 +39,35 @@ func UpdateAllRepoCommitCnt(ctx *macaron.Context) { | |||
| } | |||
| func RepoStatisticManually(ctx *macaron.Context) { | |||
| date := ctx.Query("date") | |||
| date := ctx.Params("date") | |||
| repo.RepoStatisticDaily(date) | |||
| repo.SummaryStatisticDaily(date) | |||
| repo.TimingCountDataByDate(date) | |||
| } | |||
| func UpdateRepoVisit(ctx *macaron.Context) { | |||
| date := ctx.Params("date") | |||
| log.Info("date(%s)", date) | |||
| repos, err := models.GetAllRepositories() | |||
| if err != nil { | |||
| log.Error("GetAllRepositories failed:%v", err.Error(), ctx.Data["MsgID"]) | |||
| ctx.JSON(http.StatusInternalServerError, map[string]string{ | |||
| "error_msg": "GetAllRepositories failed", | |||
| }) | |||
| return | |||
| } | |||
| for i, repoStat := range repos { | |||
| log.Info("%d:begin UpdateRepoVisits(id = %d, name = %s)", i, repoStat.ID, repoStat.Name) | |||
| if err = repo.UpdateRepoVisits(ctx, repoStat, date); err != nil { | |||
| log.Error("UpdateRepoVisits(id = %d, name = %s) failed:%v", repoStat.ID, repoStat.Name, err.Error()) | |||
| continue | |||
| } | |||
| log.Info("%d:finish UpdateRepoVisits(id = %d, name = %s)", i, repoStat.ID, repoStat.Name) | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]string{ | |||
| "error_msg": "", | |||
| }) | |||
| } | |||
| @@ -360,6 +360,7 @@ func GetPresignedPutObjectURL(ctx *context.Context) { | |||
| // AddAttachment response for add attachment record | |||
| func AddAttachment(ctx *context.Context) { | |||
| typeCloudBrain := ctx.QueryInt("type") | |||
| fileName := ctx.Query("file_name") | |||
| err := checkTypeCloudBrain(typeCloudBrain) | |||
| if err != nil { | |||
| ctx.ServerError("checkTypeCloudBrain failed", err) | |||
| @@ -375,7 +376,7 @@ func AddAttachment(ctx *context.Context) { | |||
| return | |||
| } | |||
| } else { | |||
| has, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(uuid) + "/" + uuid) | |||
| has, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(uuid) + "/" + fileName) | |||
| if err != nil { | |||
| ctx.ServerError("ObsHasObject", err) | |||
| return | |||
| @@ -391,7 +392,7 @@ func AddAttachment(ctx *context.Context) { | |||
| UUID: uuid, | |||
| UploaderID: ctx.User.ID, | |||
| IsPrivate: true, | |||
| Name: ctx.Query("file_name"), | |||
| Name: fileName, | |||
| Size: ctx.QueryInt64("size"), | |||
| DatasetID: ctx.QueryInt64("dataset_id"), | |||
| Type: typeCloudBrain, | |||
| @@ -479,6 +480,7 @@ func UpdateAttachmentDecompressState(ctx *context.Context) { | |||
| func GetSuccessChunks(ctx *context.Context) { | |||
| fileMD5 := ctx.Query("md5") | |||
| typeCloudBrain := ctx.QueryInt("type") | |||
| fileName := ctx.Query("file_name") | |||
| var chunks string | |||
| err := checkTypeCloudBrain(typeCloudBrain) | |||
| @@ -510,7 +512,7 @@ func GetSuccessChunks(ctx *context.Context) { | |||
| return | |||
| } | |||
| } else { | |||
| isExist, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(fileChunk.UUID) + "/" + fileChunk.UUID) | |||
| isExist, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(fileChunk.UUID) + "/" + fileName) | |||
| if err != nil { | |||
| ctx.ServerError("ObsHasObject failed", err) | |||
| return | |||
| @@ -10,6 +10,7 @@ import ( | |||
| "os" | |||
| "os/exec" | |||
| "regexp" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| @@ -199,12 +200,12 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||
| command := form.Command | |||
| uuid := form.Attachment | |||
| jobType := form.JobType | |||
| gpuQueue := setting.JobType | |||
| gpuQueue := form.GpuType | |||
| codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | |||
| resourceSpecId := form.ResourceSpecId | |||
| if !jobNamePattern.MatchString(jobName) { | |||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainNew, &form) | |||
| return | |||
| } | |||
| @@ -242,7 +243,6 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||
| benchmarkPath := setting.JobPath + jobName + cloudbrain.BenchMarkMountPath | |||
| if setting.IsBenchmarkEnabled && jobType == string(models.JobTypeBenchmark) { | |||
| gpuQueue = form.GpuType | |||
| var gpuType string | |||
| for _, gpuInfo := range gpuInfos.GpuInfo { | |||
| if gpuInfo.Queue == gpuQueue { | |||
| @@ -528,6 +528,15 @@ func CloudBrainShowModels(ctx *context.Context) { | |||
| return | |||
| } | |||
| for i, fileInfo := range fileInfos { | |||
| temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime) | |||
| fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05") | |||
| } | |||
| sort.Slice(fileInfos, func(i, j int) bool { | |||
| return fileInfos[i].ModTime > fileInfos[j].ModTime | |||
| }) | |||
| ctx.Data["Path"] = dirArray | |||
| ctx.Data["Dirs"] = fileInfos | |||
| ctx.Data["task"] = task | |||
| @@ -317,6 +317,12 @@ func HTTP(ctx *context.Context) { | |||
| go repo.IncreaseCloneCnt() | |||
| } | |||
| if ctx.Req.Method == "POST" { | |||
| if strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") { | |||
| go repo.IncreaseGitCloneCnt() | |||
| } | |||
| } | |||
| w := ctx.Resp | |||
| r := ctx.Req.Request | |||
| cfg := &serviceConfig{ | |||
| @@ -1,20 +1,26 @@ | |||
| package repo | |||
| import ( | |||
| "errors" | |||
| "time" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/modules/normalization" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/normalization" | |||
| "code.gitea.io/gitea/modules/repository" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/services/mailer" | |||
| "gitea.com/macaron/macaron" | |||
| ) | |||
| //auto daily or manually | |||
| func StatisticAuto() { | |||
| RepoStatisticAuto() | |||
| TimingCountData() | |||
| } | |||
| //auto daily | |||
| func RepoStatisticAuto() { | |||
| log.Info("", time.Now()) | |||
| yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | |||
| setting.UpdateRadarMap() | |||
| RepoStatisticDaily(yesterday) | |||
| @@ -24,14 +30,17 @@ func RepoStatisticDaily(date string) { | |||
| log.Info("%s", date) | |||
| log.Info("begin Repo Statistic") | |||
| t, _ := time.Parse("2006-01-02", date) | |||
| warnEmailMessage := "项目统计信息入库失败,请尽快定位。" | |||
| if err := models.DeleteRepoStatDaily(date); err != nil { | |||
| log.Error("DeleteRepoStatDaily failed: %v", err.Error()) | |||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||
| return | |||
| } | |||
| repos, err := models.GetAllRepositories() | |||
| if err != nil { | |||
| log.Error("GetAllRepositories failed: %v", err.Error()) | |||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||
| return | |||
| } | |||
| @@ -40,12 +49,14 @@ func RepoStatisticDaily(date string) { | |||
| var minRepoRadar models.RepoStatistic | |||
| var maxRepoRadar models.RepoStatistic | |||
| for i, repo := range repos { | |||
| log.Info("start statistic: %s", repo.Name) | |||
| var numDevMonths, numWikiViews, numContributor, numKeyContributor, numCommitsGrowth, numCommitLinesGrowth, numContributorsGrowth int64 | |||
| isInitMinMaxRadar := false | |||
| for _, repo := range repos { | |||
| log.Info("start statistic: %s", getDistinctProjectName(repo)) | |||
| var numDevMonths, numWikiViews, numContributor, numKeyContributor, numCommitsGrowth, numCommitLinesGrowth, numContributorsGrowth, numCommits int64 | |||
| repoGitStat, err := models.GetRepoKPIStats(repo) | |||
| if err != nil { | |||
| log.Error("GetRepoKPIStats failed: %s", repo.Name) | |||
| log.Error("GetRepoKPIStats failed: %s", getDistinctProjectName(repo)) | |||
| } else { | |||
| numDevMonths = repoGitStat.DevelopAge | |||
| numKeyContributor = repoGitStat.KeyContributors | |||
| @@ -54,36 +65,40 @@ func RepoStatisticDaily(date string) { | |||
| numCommitsGrowth = repoGitStat.CommitsAdded | |||
| numCommitLinesGrowth = repoGitStat.CommitLinesModified | |||
| numContributorsGrowth = repoGitStat.ContributorsAdded | |||
| numCommits = repoGitStat.Commits | |||
| } | |||
| var issueFixedRate float32 | |||
| if repo.NumIssues != 0 { | |||
| issueFixedRate = float32(repo.NumClosedIssues) / float32(repo.NumIssues) | |||
| } else { | |||
| issueFixedRate = 1.0 | |||
| } | |||
| var numVersions int64 | |||
| numVersions, err = models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{}) | |||
| if err != nil { | |||
| log.Error("GetReleaseCountByRepoID failed(%s): %v", repo.Name, err) | |||
| log.Error("GetReleaseCountByRepoID failed(%s): %v", getDistinctProjectName(repo), err) | |||
| } | |||
| var datasetSize int64 | |||
| datasetSize, err = getDatasetSize(repo) | |||
| if err != nil { | |||
| log.Error("getDatasetSize failed(%s): %v", repo.Name, err) | |||
| log.Error("getDatasetSize failed(%s): %v", getDistinctProjectName(repo), err) | |||
| } | |||
| var numComments int64 | |||
| numComments, err = models.GetCommentCountByRepoID(repo.ID) | |||
| if err != nil { | |||
| log.Error("GetCommentCountByRepoID failed(%s): %v", repo.Name, err) | |||
| log.Error("GetCommentCountByRepoID failed(%s): %v", getDistinctProjectName(repo), err) | |||
| } | |||
| beginTime, endTime := getStatTime(date) | |||
| var numVisits int | |||
| numVisits, err = repository.AppointProjectView(repo.OwnerName, repo.Name, beginTime, endTime) | |||
| if err != nil { | |||
| log.Error("AppointProjectView failed(%s): %v", repo.Name, err) | |||
| log.Error("AppointProjectView failed(%s): %v", getDistinctProjectName(repo), err) | |||
| } | |||
| repoStat := models.RepoStatistic{ | |||
| @@ -91,8 +106,11 @@ func RepoStatisticDaily(date string) { | |||
| 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), | |||
| @@ -103,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, | |||
| @@ -144,14 +162,16 @@ func RepoStatisticDaily(date string) { | |||
| } | |||
| if _, err = models.InsertRepoStat(&repoStat); err != nil { | |||
| log.Error("InsertRepoStat failed(%s): %v", repo.Name, err) | |||
| log.Error("failed statistic: %s", repo.Name) | |||
| log.Error("InsertRepoStat failed(%s): %v", getDistinctProjectName(repo), err) | |||
| log.Error("failed statistic: %s", getDistinctProjectName(repo)) | |||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||
| continue | |||
| } | |||
| 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), | |||
| @@ -162,74 +182,91 @@ 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 | |||
| } | |||
| } | |||
| log.Info("finish statistic: %s", repo.Name) | |||
| log.Info("finish statistic: %s", getDistinctProjectName(repo)) | |||
| } | |||
| //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) | |||
| } | |||
| @@ -237,6 +274,10 @@ func RepoStatisticDaily(date string) { | |||
| } | |||
| func getDistinctProjectName(repo *models.Repository) string { | |||
| return repo.OwnerName + "/" + repo.Name | |||
| } | |||
| func getDatasetSize(repo *models.Repository) (int64, error) { | |||
| dataset, err := models.GetDatasetByRepo(repo) | |||
| if err != nil { | |||
| @@ -257,3 +298,29 @@ func getStatTime(timeStr string) (string, string) { | |||
| return beginTime, endTime | |||
| } | |||
| func UpdateRepoVisits(ctx *macaron.Context, repo *models.Repository, date string) error { | |||
| beginTime, endTime := getStatTime(date) | |||
| var numVisits int | |||
| numVisits, err := repository.AppointProjectView(repo.OwnerName, repo.Name, beginTime, endTime) | |||
| if err != nil { | |||
| log.Error("AppointProjectView failed(%s): %v", getDistinctProjectName(repo), err) | |||
| return err | |||
| } | |||
| repoStat, err := models.GetRepoStatisticByDate(date, repo.ID) | |||
| if err != nil { | |||
| log.Error("GetRepoStatisticByDate failed(%s): %v", getDistinctProjectName(repo), err) | |||
| return err | |||
| } else if len(repoStat) != 1 { | |||
| log.Error("GetRepoStatisticByDate failed(%s): %v", getDistinctProjectName(repo), err) | |||
| return errors.New("not find repo") | |||
| } | |||
| repoStat[0].NumVisits = int64(numVisits) | |||
| if err = models.UpdateRepoStatVisits(repoStat[0]); err != nil { | |||
| log.Error("UpdateRepoStatVisits failed(%s): %v", getDistinctProjectName(repo), err) | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| @@ -1,14 +1,157 @@ | |||
| package repo | |||
| import ( | |||
| "fmt" | |||
| "net/http" | |||
| "net/url" | |||
| "time" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/git" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/services/mailer" | |||
| "github.com/360EntSecGroup-Skylar/excelize/v2" | |||
| ) | |||
| func TimingCountDataByDate(date string) { | |||
| func QueryUserStaticDataPage(ctx *context.Context) { | |||
| startDate := ctx.Query("startDate") | |||
| endDate := ctx.Query("endDate") | |||
| page := ctx.QueryInt("page") | |||
| if page <= 0 { | |||
| page = 1 | |||
| } | |||
| pageSize := ctx.QueryInt("pageSize") | |||
| if pageSize <= 0 { | |||
| pageSize = setting.UI.IssuePagingNum | |||
| } | |||
| userName := ctx.Query("userName") | |||
| IsReturnFile := ctx.QueryBool("IsReturnFile") | |||
| log.Info("startDate=" + startDate + " endDate=" + endDate + " userName=" + userName + " page=" + fmt.Sprint(page)) | |||
| var startTime time.Time | |||
| var endTime time.Time | |||
| var isAll bool | |||
| if startDate == "all" { | |||
| isAll = true | |||
| startTime = time.Now() | |||
| endTime = time.Now() | |||
| } else { | |||
| startTime, _ = time.ParseInLocation("2006-01-02", startDate, time.Local) | |||
| startTime = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 12, 0, 0, 0, startTime.Location()) | |||
| settingStartTime, _ := time.Parse("2006-01-02", setting.RadarMap.RecordBeginTime) | |||
| if startTime.Unix() < settingStartTime.Unix() { | |||
| startTime = settingStartTime | |||
| startDate = settingStartTime.Format("2006-01-02") | |||
| } | |||
| endTime, _ = time.ParseInLocation("2006-01-02", endDate, time.Local) | |||
| endTime = endTime.AddDate(0, 0, 1) | |||
| endTime = time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 0, startTime.Location()) | |||
| isAll = false | |||
| log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) | |||
| } | |||
| if IsReturnFile { | |||
| page = -1 | |||
| pageSize = -1 | |||
| } | |||
| pageOpts := &models.UserBusinessAnalysisQueryOptions{ | |||
| ListOptions: models.ListOptions{ | |||
| Page: page, | |||
| PageSize: pageSize, | |||
| }, | |||
| UserName: userName, | |||
| StartTime: startTime.Unix(), | |||
| EndTime: endTime.Unix(), | |||
| IsAll: isAll, | |||
| } | |||
| if IsReturnFile { | |||
| re, count := models.QueryUserStaticDataAll(pageOpts) | |||
| log.Info("return count=" + fmt.Sprint(count)) | |||
| //writer exec file. | |||
| xlsx := excelize.NewFile() | |||
| sheetName := ctx.Tr("user.static.sheetname") | |||
| index := xlsx.NewSheet(sheetName) | |||
| xlsx.DeleteSheet("Sheet1") | |||
| dataHeader := map[string]string{ | |||
| "A1": ctx.Tr("user.static.id"), | |||
| "B1": ctx.Tr("user.static.name"), | |||
| "C1": ctx.Tr("user.static.codemergecount"), | |||
| "D1": ctx.Tr("user.static.commitcount"), | |||
| "E1": ctx.Tr("user.static.issuecount"), | |||
| "F1": ctx.Tr("user.static.commentcount"), | |||
| "G1": ctx.Tr("user.static.focusrepocount"), | |||
| "H1": ctx.Tr("user.static.starrepocount"), | |||
| "I1": ctx.Tr("user.static.logincount"), | |||
| "J1": ctx.Tr("user.static.watchedcount"), | |||
| "K1": ctx.Tr("user.static.commitcodesize"), | |||
| "L1": ctx.Tr("user.static.solveissuecount"), | |||
| "M1": ctx.Tr("user.static.encyclopediascount"), | |||
| "N1": ctx.Tr("user.static.createrepocount"), | |||
| "O1": ctx.Tr("user.static.openiindex"), | |||
| "P1": ctx.Tr("user.static.registdate"), | |||
| "Q1": ctx.Tr("user.static.countdate"), | |||
| } | |||
| for k, v := range dataHeader { | |||
| //设置单元格的值 | |||
| xlsx.SetCellValue(sheetName, k, v) | |||
| } | |||
| for i, userRecord := range re { | |||
| rows := fmt.Sprint(i + 2) | |||
| xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID) | |||
| xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name) | |||
| xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount) | |||
| xlsx.SetCellValue(sheetName, "D"+rows, userRecord.CommitCount) | |||
| xlsx.SetCellValue(sheetName, "E"+rows, userRecord.IssueCount) | |||
| xlsx.SetCellValue(sheetName, "F"+rows, userRecord.CommentCount) | |||
| xlsx.SetCellValue(sheetName, "G"+rows, userRecord.FocusRepoCount) | |||
| xlsx.SetCellValue(sheetName, "H"+rows, userRecord.StarRepoCount) | |||
| xlsx.SetCellValue(sheetName, "I"+rows, userRecord.LoginCount) | |||
| xlsx.SetCellValue(sheetName, "J"+rows, userRecord.WatchedCount) | |||
| xlsx.SetCellValue(sheetName, "K"+rows, userRecord.CommitCodeSize) | |||
| xlsx.SetCellValue(sheetName, "L"+rows, userRecord.SolveIssueCount) | |||
| xlsx.SetCellValue(sheetName, "M"+rows, userRecord.EncyclopediasCount) | |||
| xlsx.SetCellValue(sheetName, "N"+rows, userRecord.CreateRepoCount) | |||
| xlsx.SetCellValue(sheetName, "O"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex)) | |||
| formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05") | |||
| xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3]) | |||
| formatTime = userRecord.DataDate | |||
| xlsx.SetCellValue(sheetName, "Q"+rows, formatTime+" 00:01") | |||
| } | |||
| //设置默认打开的表单 | |||
| xlsx.SetActiveSheet(index) | |||
| filename := sheetName + "_" + ctx.Tr("user.static.all") + ".xlsx" | |||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename)) | |||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
| if _, err := xlsx.WriteTo(ctx.Resp); err != nil { | |||
| log.Info("writer exel error." + err.Error()) | |||
| } | |||
| } else { | |||
| mapInterface := make(map[string]interface{}) | |||
| re, count := models.QueryUserStaticDataPage(pageOpts) | |||
| mapInterface["data"] = re | |||
| mapInterface["count"] = count | |||
| ctx.JSON(http.StatusOK, mapInterface) | |||
| } | |||
| } | |||
| func TimingCountDataByDateAndReCount(date string, isReCount bool) { | |||
| if date == "refreshAll" { | |||
| models.RefreshUserStaticAllTabel() | |||
| return | |||
| } | |||
| t, _ := time.Parse("2006-01-02", date) | |||
| startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | |||
| @@ -18,10 +161,11 @@ func TimingCountDataByDate(date string) { | |||
| //query wiki data | |||
| log.Info("start to time count data") | |||
| wikiMap := make(map[string]int) | |||
| warnEmailMessage := "用户统计信息入库失败,请尽快定位。" | |||
| repoList, err := models.GetAllRepositories() | |||
| if err != nil { | |||
| log.Error("query repo error.") | |||
| log.Error("query repo error." + err.Error()) | |||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||
| return | |||
| } | |||
| log.Info("start to query wiki data") | |||
| @@ -56,16 +200,25 @@ func TimingCountDataByDate(date string) { | |||
| } | |||
| } | |||
| //other user info data | |||
| models.CounDataByDate(wikiMap, startTime, endTime) | |||
| err = models.CounDataByDateAndReCount(wikiMap, startTime, endTime, isReCount) | |||
| if err != nil { | |||
| log.Error("count user info error." + err.Error()) | |||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||
| } | |||
| if isReCount { | |||
| models.RefreshUserStaticAllTabel() | |||
| } | |||
| } | |||
| func TimingCountData() { | |||
| func TimingCountDataByDate(date string) { | |||
| TimingCountDataByDateAndReCount(date, true) | |||
| } | |||
| 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) | |||
| TimingCountDataByDateAndReCount(startTime, false) | |||
| } | |||
| @@ -12,8 +12,10 @@ import ( | |||
| "fmt" | |||
| gotemplate "html/template" | |||
| "io/ioutil" | |||
| "net/http" | |||
| "net/url" | |||
| "path" | |||
| "sort" | |||
| "strings" | |||
| "time" | |||
| @@ -31,11 +33,12 @@ import ( | |||
| ) | |||
| const ( | |||
| tplRepoEMPTY base.TplName = "repo/empty" | |||
| tplRepoHome base.TplName = "repo/home" | |||
| tplWatchers base.TplName = "repo/watchers" | |||
| tplForks base.TplName = "repo/forks" | |||
| tplMigrating base.TplName = "repo/migrating" | |||
| tplRepoEMPTY base.TplName = "repo/empty" | |||
| tplRepoHome base.TplName = "repo/home" | |||
| tplWatchers base.TplName = "repo/watchers" | |||
| tplForks base.TplName = "repo/forks" | |||
| tplMigrating base.TplName = "repo/migrating" | |||
| tplContributors base.TplName = "repo/contributors" | |||
| ) | |||
| type namedBlob struct { | |||
| @@ -243,6 +246,11 @@ func renderDirectory(ctx *context.Context, treeLink string) { | |||
| ctx.Data["ReadmeInList"] = true | |||
| ctx.Data["ReadmeExist"] = true | |||
| ctx.Data["FileIsSymlink"] = readmeFile.isSymlink | |||
| ctx.Data["ReadmeName"] = readmeFile.name | |||
| if ctx.Repo.CanEnableEditor() { | |||
| ctx.Data["CanEditFile"] = true | |||
| } | |||
| dataRc, err := readmeFile.blob.DataAsync() | |||
| if err != nil { | |||
| @@ -570,19 +578,29 @@ func safeURL(address string) string { | |||
| } | |||
| type ContributorInfo struct { | |||
| UserInfo *models.User // nil for contributor who is not a registered user | |||
| Email string | |||
| CommitCnt int | |||
| UserInfo *models.User // nil for contributor who is not a registered user | |||
| RelAvatarLink string `json:"rel_avatar_link"` | |||
| UserName string `json:"user_name"` | |||
| Email string `json:"email"` | |||
| CommitCnt int `json:"commit_cnt"` | |||
| } | |||
| type GetContributorsInfo struct { | |||
| ErrorCode int `json:"error_code"` | |||
| ErrorMsg string `json:"error_msg"` | |||
| Count int `json:"count"` | |||
| ContributorInfo []*ContributorInfo `json:"contributor_info"` | |||
| } | |||
| func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo{ | |||
| func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo { | |||
| for _, c := range contributorInfos { | |||
| if strings.Compare(c.Email,email) == 0 { | |||
| if strings.Compare(c.Email, email) == 0 { | |||
| return c | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // Home render repository home page | |||
| func Home(ctx *context.Context) { | |||
| if len(ctx.Repo.Units) > 0 { | |||
| @@ -591,35 +609,41 @@ func Home(ctx *context.Context) { | |||
| if err == nil && contributors != nil { | |||
| startTime := time.Now() | |||
| var contributorInfos []*ContributorInfo | |||
| contributorInfoHash:= make(map[string]*ContributorInfo) | |||
| contributorInfoHash := make(map[string]*ContributorInfo) | |||
| count := 0 | |||
| for _, c := range contributors { | |||
| if strings.Compare(c.Email,"") == 0 { | |||
| if count >= 25 { | |||
| continue | |||
| } | |||
| if strings.Compare(c.Email, "") == 0 { | |||
| continue | |||
| } | |||
| // get user info from committer email | |||
| user, err := models.GetUserByActivateEmail(c.Email) | |||
| if err == nil { | |||
| // committer is system user, get info through user's primary email | |||
| if existedContributorInfo,ok:=contributorInfoHash[user.Email];ok { | |||
| if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok { | |||
| // existed: same primary email, different committer name | |||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||
| }else{ | |||
| } else { | |||
| // new committer info | |||
| var newContributor = &ContributorInfo{ | |||
| user, user.Email,c.CommitCnt, | |||
| user, user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt, | |||
| } | |||
| contributorInfos = append(contributorInfos, newContributor ) | |||
| count++ | |||
| contributorInfos = append(contributorInfos, newContributor) | |||
| contributorInfoHash[user.Email] = newContributor | |||
| } | |||
| } else { | |||
| // committer is not system user | |||
| if existedContributorInfo,ok:=contributorInfoHash[c.Email];ok { | |||
| if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok { | |||
| // existed: same primary email, different committer name | |||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||
| }else{ | |||
| } else { | |||
| var newContributor = &ContributorInfo{ | |||
| user, c.Email,c.CommitCnt, | |||
| user, "", "",c.Email, c.CommitCnt, | |||
| } | |||
| count++ | |||
| contributorInfos = append(contributorInfos, newContributor) | |||
| contributorInfoHash[c.Email] = newContributor | |||
| } | |||
| @@ -627,7 +651,7 @@ func Home(ctx *context.Context) { | |||
| } | |||
| ctx.Data["ContributorInfo"] = contributorInfos | |||
| var duration = time.Since(startTime) | |||
| log.Info("getContributorInfo cost: %v seconds",duration.Seconds()) | |||
| log.Info("getContributorInfo cost: %v seconds", duration.Seconds()) | |||
| } | |||
| if ctx.Repo.Repository.IsBeingCreated() { | |||
| task, err := models.GetMigratingTask(ctx.Repo.Repository.ID) | |||
| @@ -694,13 +718,13 @@ func renderLicense(ctx *context.Context) { | |||
| log.Error("failed to get license content: %v, err:%v", f, err) | |||
| continue | |||
| } | |||
| if bytes.Compare(buf,license) == 0 { | |||
| log.Info("got matched license:%v",f) | |||
| if bytes.Compare(buf, license) == 0 { | |||
| log.Info("got matched license:%v", f) | |||
| ctx.Data["LICENSE"] = f | |||
| return | |||
| } | |||
| } | |||
| log.Info("not found matched license,repo:%v",ctx.Repo.Repository.Name) | |||
| log.Info("not found matched license,repo:%v", ctx.Repo.Repository.Name) | |||
| } | |||
| func renderLanguageStats(ctx *context.Context) { | |||
| @@ -800,32 +824,29 @@ func renderCode(ctx *context.Context) { | |||
| */ | |||
| baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath()) | |||
| defer baseGitRepo.Close() | |||
| var compareInfo *git.CompareInfo | |||
| if err != nil { | |||
| log.Error("error open baseRepo:%s",ctx.Repo.Repository.BaseRepo.RepoPath()) | |||
| log.Error("error open baseRepo:%s", ctx.Repo.Repository.BaseRepo.RepoPath()) | |||
| ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error | |||
| }else{ | |||
| if _,error:= baseGitRepo.GetBranch(ctx.Repo.BranchName);error==nil{ | |||
| } else { | |||
| if _, error := baseGitRepo.GetBranch(ctx.Repo.BranchName); error == nil { | |||
| //base repo has the same branch, then compare between current repo branch and base repo's branch | |||
| compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName | |||
| ctx.SetParams("*",compareUrl) | |||
| compareInfo, err = baseGitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), ctx.Repo.BranchName, ctx.Repo.BranchName) | |||
| ctx.Data["UpstreamSameBranchName"] = true | |||
| }else{ | |||
| } else { | |||
| //else, compare between current repo branch and base repo's default branch | |||
| compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch | |||
| ctx.SetParams("*",compareUrl) | |||
| compareInfo, err = baseGitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), ctx.Repo.BranchName, ctx.Repo.Repository.BaseRepo.DefaultBranch) | |||
| ctx.Data["UpstreamSameBranchName"] = false | |||
| } | |||
| _, _, headGitRepo, compareInfo, _, _ := ParseCompareInfo(ctx) | |||
| defer headGitRepo.Close() | |||
| if compareInfo!= nil { | |||
| if compareInfo.Commits!=nil { | |||
| log.Info("compareInfoCommits数量:%d",compareInfo.Commits.Len()) | |||
| if err==nil && compareInfo != nil { | |||
| if compareInfo.Commits != nil { | |||
| log.Info("compareInfoCommits数量:%d", compareInfo.Commits.Len()) | |||
| ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len() | |||
| }else{ | |||
| } else { | |||
| log.Info("compareInfo nothing different") | |||
| ctx.Data["FetchUpstreamCnt"] = 0 | |||
| } | |||
| }else{ | |||
| } else { | |||
| ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error | |||
| } | |||
| } | |||
| @@ -893,3 +914,68 @@ func Forks(ctx *context.Context) { | |||
| ctx.HTML(200, tplForks) | |||
| } | |||
| func Contributors(ctx *context.Context) { | |||
| ctx.Data["PageIsViewCode"] = true | |||
| ctx.HTML(http.StatusOK, tplContributors) | |||
| } | |||
| func ContributorsAPI(ctx *context.Context) { | |||
| count := 0 | |||
| errorCode := 0 | |||
| errorMsg := "" | |||
| contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath()) | |||
| var contributorInfos []*ContributorInfo | |||
| if err == nil && contributors != nil { | |||
| contributorInfoHash := make(map[string]*ContributorInfo) | |||
| for _, c := range contributors { | |||
| if strings.Compare(c.Email, "") == 0 { | |||
| continue | |||
| } | |||
| // get user info from committer email | |||
| user, err := models.GetUserByActivateEmail(c.Email) | |||
| if err == nil { | |||
| // committer is system user, get info through user's primary email | |||
| if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok { | |||
| // existed: same primary email, different committer name | |||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||
| } else { | |||
| // new committer info | |||
| var newContributor = &ContributorInfo{ | |||
| user, user.RelAvatarLink(),user.Name, user.Email,c.CommitCnt, | |||
| } | |||
| count++ | |||
| contributorInfos = append(contributorInfos, newContributor) | |||
| contributorInfoHash[user.Email] = newContributor | |||
| } | |||
| } else { | |||
| // committer is not system user | |||
| if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok { | |||
| // existed: same primary email, different committer name | |||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||
| } else { | |||
| var newContributor = &ContributorInfo{ | |||
| user, "", "",c.Email,c.CommitCnt, | |||
| } | |||
| count++ | |||
| contributorInfos = append(contributorInfos, newContributor) | |||
| contributorInfoHash[c.Email] = newContributor | |||
| } | |||
| } | |||
| } | |||
| sort.Slice(contributorInfos, func(i, j int) bool { | |||
| return contributorInfos[i].CommitCnt > contributorInfos[j].CommitCnt | |||
| }) | |||
| } else { | |||
| log.Error("GetContributors failed: %v", err) | |||
| errorCode = -1 | |||
| errorMsg = err.Error() | |||
| } | |||
| ctx.JSON(http.StatusOK, GetContributorsInfo{ | |||
| ErrorCode: errorCode, | |||
| ErrorMsg: errorMsg, | |||
| Count: count, | |||
| ContributorInfo: contributorInfos, | |||
| }) | |||
| } | |||
| @@ -311,7 +311,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Head("/", func() string { | |||
| return "" | |||
| }) | |||
| m.Get("/", routers.Dashboard) | |||
| m.Get("/", routers.Home) | |||
| m.Get("/dashboard", routers.Dashboard) | |||
| m.Group("/explore", func() { | |||
| m.Get("", func(ctx *context.Context) { | |||
| @@ -325,6 +325,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Get("/organizations", routers.ExploreOrganizations) | |||
| m.Get("/code", routers.ExploreCode) | |||
| m.Get("/images", routers.ExploreImages) | |||
| m.Get("/data_analysis", routers.ExploreDataAnalysis) | |||
| }, ignSignIn) | |||
| m.Combo("/install", routers.InstallInit).Get(routers.Install). | |||
| Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) | |||
| @@ -615,6 +616,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| //reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | |||
| // ***** START: Organization ***** | |||
| m.Group("/org", func() { | |||
| m.Group("/:org", func() { | |||
| m.Get("/members", org.Members) | |||
| }, context.OrgAssignment()) | |||
| }) | |||
| m.Group("/org", func() { | |||
| m.Group("", func() { | |||
| m.Get("/create", org.Create) | |||
| @@ -625,7 +631,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Get("/dashboard", user.Dashboard) | |||
| m.Get("/^:type(issues|pulls)$", user.Issues) | |||
| m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones) | |||
| m.Get("/members", org.Members) | |||
| //m.Get("/members", org.Members) | |||
| m.Post("/members/action/:action", org.MembersAction) | |||
| m.Get("/teams", org.Teams) | |||
| @@ -786,9 +792,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) | |||
| m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) | |||
| m.Get("/tool/query_user_static_page", adminReq, repo.QueryUserStaticDataPage) | |||
| // Grouping for those endpoints not requiring authentication | |||
| m.Group("/:username/:reponame", func() { | |||
| m.Get("/contributors", repo.Contributors) | |||
| m.Get("/contributors/list", repo.ContributorsAPI) | |||
| m.Group("/milestone", func() { | |||
| m.Get("/:id", repo.MilestoneIssuesAndPulls) | |||
| }, reqRepoIssuesOrPullsReader, context.RepoRef()) | |||
| @@ -962,16 +970,6 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| }, context.RepoRef()) | |||
| m.Group("/modelarts", func() { | |||
| // m.Get("", reqRepoCloudBrainReader, repo.ModelArtsIndex) | |||
| // m.Group("/:jobid", func() { | |||
| // m.Get("", reqRepoCloudBrainReader, repo.ModelArtsShow) | |||
| // m.Get("/debug", reqRepoCloudBrainReader, repo.ModelArtsDebug) | |||
| // m.Post("/stop", reqRepoCloudBrainWriter, repo.ModelArtsStop) | |||
| // m.Post("/del", reqRepoCloudBrainWriter, repo.ModelArtsDel) | |||
| // }) | |||
| // m.Get("/create", reqRepoCloudBrainWriter, repo.ModelArtsNew) | |||
| // m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsForm{}), repo.ModelArtsCreate) | |||
| m.Group("/notebook", func() { | |||
| m.Get("", reqRepoCloudBrainReader, repo.NotebookIndex) | |||
| m.Group("/:jobid", func() { | |||
| @@ -990,12 +988,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow) | |||
| m.Post("/stop", reqRepoCloudBrainWriter, repo.TrainJobStop) | |||
| m.Post("/del", reqRepoCloudBrainWriter, repo.TrainJobDel) | |||
| m.Get("/log", reqRepoCloudBrainReader, repo.TrainJobGetLog) | |||
| m.Get("/models", reqRepoCloudBrainReader, repo.TrainJobShowModels) | |||
| m.Get("/download_model", reqRepoCloudBrainReader, repo.TrainJobDownloadModel) | |||
| m.Get("/model_download", reqRepoCloudBrainReader, repo.ModelDownload) | |||
| m.Get("/create_version", reqRepoCloudBrainReader, repo.TrainJobNewVersion) | |||
| m.Post("/create_version", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion) | |||
| }) | |||
| m.Get("/create", reqRepoCloudBrainReader, repo.TrainJobNew) | |||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate) | |||
| m.Get("/para-config-list", reqRepoCloudBrainReader, repo.TrainJobGetConfigList) | |||
| }) | |||
| }, context.RepoRef()) | |||
| @@ -544,7 +544,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR | |||
| if err := models.UpdateUserCols(u, "language"); err != nil { | |||
| log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language)) | |||
| return setting.AppSubURL + "/" | |||
| return setting.AppSubURL + "/dashboard" | |||
| } | |||
| } else { | |||
| // Language setting of the user use the one previously set | |||
| @@ -562,7 +562,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR | |||
| u.SetLastLogin() | |||
| if err := models.UpdateUserCols(u, "last_login_unix"); err != nil { | |||
| ctx.ServerError("UpdateUserCols", err) | |||
| return setting.AppSubURL + "/" | |||
| return setting.AppSubURL + "/dashboard" | |||
| } | |||
| if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) { | |||
| @@ -574,9 +574,9 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR | |||
| } | |||
| if obeyRedirect { | |||
| ctx.Redirect(setting.AppSubURL + "/") | |||
| ctx.Redirect(setting.AppSubURL + "/dashboard") | |||
| } | |||
| return setting.AppSubURL + "/" | |||
| return setting.AppSubURL + "/dashboard" | |||
| } | |||
| // SignInOAuth handles the OAuth2 login buttons | |||
| @@ -115,6 +115,22 @@ func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAdd | |||
| SendAsync(msg) | |||
| } | |||
| func SendWarnNotifyMail(emails []string, message string) { | |||
| if setting.MailService == nil { | |||
| log.Warn("SendWarnNotifyMail is being invoked but mail service hasn't been initialized") | |||
| return | |||
| } | |||
| if len(emails) == 0 { | |||
| log.Warn("SendWarnNotifyMail is being invoked but do not have email to send") | |||
| return | |||
| } | |||
| msg := NewMessage(emails, message, message) | |||
| msg.Info = fmt.Sprintf(message) | |||
| SendAsync(msg) | |||
| } | |||
| // SendRegisterNotifyMail triggers a notify e-mail by admin created a account. | |||
| func SendRegisterNotifyMail(locale Locale, u *models.User) { | |||
| if setting.MailService == nil { | |||
| @@ -0,0 +1,42 @@ | |||
| <footer> | |||
| <div class="ui fluid container" style="padding: 0px 10px;"> | |||
| <div class="ui grid"> | |||
| <div class="sixteen wide mobile eight wide tablet eight wide computer column"> | |||
| <div class="ui three column grid"> | |||
| <div class="column ui vertical text menu"> | |||
| <div class="header item">{{.i18n.Tr "custom.head.community"}}</div> | |||
| <a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">{{.i18n.Tr "custom.foot.council"}}</a> | |||
| <a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">{{.i18n.Tr "custom.foot.technical_committee"}}</a> | |||
| <a href="https://openi.org.cn/html/Club/2019/0228/17.html" class="item">{{.i18n.Tr "custom.foot.join"}}</a> | |||
| </div> | |||
| <div class="column ui vertical text menu"> | |||
| <div class="header item">{{.i18n.Tr "custom.foot.news"}}</div> | |||
| <a href="https://openi.org.cn/html/news/dongtai/" class="item">{{.i18n.Tr "custom.foot.community_news"}}</a> | |||
| <a href="https://openi.org.cn/html/news/huodong/" class="item">{{.i18n.Tr "custom.foot.member_news"}}</a> | |||
| <a href="https://openi.org.cn/html/news/zixun/" class="item">{{.i18n.Tr "custom.foot.industry_advisory"}}</a> | |||
| </div> | |||
| <div class="column ui vertical text menu"> | |||
| <div class="header item">{{.i18n.Tr "custom.foot.help"}}</div> | |||
| <div class="ui language bottom floating slide up dropdown link item"> | |||
| <i class="world icon"></i> | |||
| <div class="text">{{.LangName}}</div> | |||
| <div class="menu"> | |||
| {{range .AllLangs}} | |||
| <a lang="{{.Lang}}" class="item {{if eq $.Lang .Lang}}active selected{{end}}" href="{{if eq $.Lang .Lang}}#{{else}}{{$.Link}}?lang={{.Lang}}{{end}}">{{.Name}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{if .EnableSwagger}}<a href="/api/swagger" class="ui item">API</a>{{end}} | |||
| {{template "custom/extra_links_footer" .}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="sixteen wide mobile eight wide tablet eight wide computer column" style=" margin:2.0rem 0"> | |||
| {{.i18n.Tr "custom.foot.copyright"}} <a href="http://beian.miit.gov.cn/" target="_blank">京ICP备18004880号</a> | |||
| <br> | |||
| {{.i18n.Tr "Powered_by 鹏城实验室云脑、"}}<a href="https://www.trustie.net/" target="_blank">Trustie确实</a>{{.i18n.Tr "、gitea"}} | |||
| <br> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </footer> | |||
| @@ -0,0 +1,48 @@ | |||
| {{/* | |||
| <html> | |||
| <body> | |||
| <div> | |||
| */}} | |||
| {{template "custom/body_inner_post" .}} | |||
| </div> | |||
| {{template "custom/body_outer_post" .}} | |||
| {{template "base/footer_content_fluid" .}} | |||
| <script src="{{StaticUrlPrefix}}/js/jquery.js?v={{MD5 AppVer}}"></script> | |||
| {{if .RequireSimpleMDE}} | |||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.js"></script> | |||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js"></script> | |||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js"></script> | |||
| <script> | |||
| CodeMirror.modeURL = "{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/%N/%N.js"; | |||
| </script> | |||
| {{end}} | |||
| <!-- Third-party libraries --> | |||
| {{if .RequireMinicolors}} | |||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js"></script> | |||
| {{end}} | |||
| {{if .RequireU2F}} | |||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/u2f/index.js"></script> | |||
| {{end}} | |||
| {{if .EnableCaptcha}} | |||
| {{if eq .CaptchaType "recaptcha"}} | |||
| <script src='{{ URLJoin .RecaptchaURL "api.js"}}' async></script> | |||
| {{end}} | |||
| {{end}} | |||
| {{if .RequireTribute}} | |||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.min.js"></script> | |||
| {{end}} | |||
| {{if .PageIsHome}} | |||
| <script rel="stylesheet" src="{{StaticUrlPrefix}}/vendor/plugins/jquery.particleground/jquery.particleground.min.js"></script> | |||
| {{end}} | |||
| <script src="{{StaticUrlPrefix}}/fomantic/semantic.min.js?v={{MD5 AppVer}}"></script> | |||
| <script src="{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script> | |||
| {{template "custom/footer" .}} | |||
| </body> | |||
| </html> | |||
| @@ -180,8 +180,8 @@ | |||
| var _hmt = _hmt || []; | |||
| (function() { | |||
| var hm = document.createElement("script"); | |||
| hm.src = "https://hm.baidu.com/hm.js?7c4ef0a24be6109ab22e63c832ab21cf"; | |||
| var s = document.getElementsByTagName("script")[0]; | |||
| hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; | |||
| var s = document.getElementsByTagName("script")[0]; | |||
| s.parentNode.insertBefore(hm, s); | |||
| })(); | |||
| </script> | |||
| @@ -0,0 +1,208 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="{{.Language}}"> | |||
| <head data-suburl="{{AppSubUrl}}"> | |||
| <meta charset="utf-8"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <meta http-equiv="x-ua-compatible" content="ie=edge"> | |||
| <title>{{if .Title}}{{.Title}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title> | |||
| <link rel="manifest" href="{{AppSubUrl}}/manifest.json" crossorigin="use-credentials"> | |||
| {{if UseServiceWorker}} | |||
| <script> | |||
| if ('serviceWorker' in navigator) { | |||
| navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) { | |||
| // Registration was successful | |||
| console.info('ServiceWorker registration successful with scope: ', registration.scope); | |||
| }, function(err) { | |||
| // registration failed :( | |||
| console.info('ServiceWorker registration failed: ', err); | |||
| }); | |||
| } | |||
| </script> | |||
| {{else}} | |||
| <script> | |||
| if ('serviceWorker' in navigator) { | |||
| navigator.serviceWorker.getRegistrations().then(function(registrations) { | |||
| registrations.forEach(function(registration) { | |||
| registration.unregister(); | |||
| console.info('ServiceWorker unregistered'); | |||
| }); | |||
| }); | |||
| } | |||
| </script> | |||
| {{end}} | |||
| <meta name="theme-color" content="{{ThemeColorMetaTag}}"> | |||
| <meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" /> | |||
| <meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" /> | |||
| <meta name="keywords" content="{{MetaKeywords}}"> | |||
| <meta name="referrer" content="no-referrer" /> | |||
| <meta name="_csrf" content="{{.CsrfToken}}" /> | |||
| {{if .IsSigned}} | |||
| <meta name="_uid" content="{{.SignedUser.ID}}" /> | |||
| {{end}} | |||
| {{if .ContextUser}} | |||
| <meta name="_context_uid" content="{{.ContextUser.ID}}" /> | |||
| {{end}} | |||
| {{if .SearchLimit}} | |||
| <meta name="_search_limit" content="{{.SearchLimit}}" /> | |||
| {{end}} | |||
| {{if .GoGetImport}} | |||
| <meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}"> | |||
| <meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}"> | |||
| {{end}} | |||
| <script> | |||
| {{SafeJS `/* | |||
| @licstart The following is the entire license notice for the | |||
| JavaScript code in this page. | |||
| Copyright (c) 2016 The Gitea Authors | |||
| Copyright (c) 2015 The Gogs Authors | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| in the Software without restriction, including without limitation the rights | |||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| copies of the Software, and to permit persons to whom the Software is | |||
| furnished to do so, subject to the following conditions: | |||
| The above copyright notice and this permission notice shall be included in | |||
| all copies or substantial portions of the Software. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
| THE SOFTWARE. | |||
| --- | |||
| Licensing information for additional javascript libraries can be found at: | |||
| {{StaticUrlPrefix}}/vendor/librejs.html | |||
| @licend The above is the entire license notice | |||
| for the JavaScript code in this page. | |||
| */`}} | |||
| </script> | |||
| <script> | |||
| window.config = { | |||
| AppSubUrl: '{{AppSubUrl}}', | |||
| StaticUrlPrefix: '{{StaticUrlPrefix}}', | |||
| csrf: '{{.CsrfToken}}', | |||
| HighlightJS: {{if .RequireHighlightJS}}true{{else}}false{{end}}, | |||
| Minicolors: {{if .RequireMinicolors}}true{{else}}false{{end}}, | |||
| SimpleMDE: {{if .RequireSimpleMDE}}true{{else}}false{{end}}, | |||
| Tribute: {{if .RequireTribute}}true{{else}}false{{end}}, | |||
| U2F: {{if .RequireU2F}}true{{else}}false{{end}}, | |||
| Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}}, | |||
| heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}}, | |||
| NotificationSettings: { | |||
| MinTimeout: {{NotificationSettings.MinTimeout}}, | |||
| TimeoutStep: {{NotificationSettings.TimeoutStep}}, | |||
| MaxTimeout: {{NotificationSettings.MaxTimeout}}, | |||
| EventSourceUpdateTime: {{NotificationSettings.EventSourceUpdateTime}}, | |||
| }, | |||
| {{if .RequireTribute}} | |||
| tributeValues: [ | |||
| {{ range .Assignees }} | |||
| {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', | |||
| name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.RelAvatarLink}}'}, | |||
| {{ end }} | |||
| ], | |||
| {{end}} | |||
| }; | |||
| </script> | |||
| <link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png"> | |||
| <link rel="mask-icon" href="{{StaticUrlPrefix}}/img/openi-safari.svg" color="#609926"> | |||
| <link rel="fluid-icon" href="{{StaticUrlPrefix}}/img/gitea-lg.png" title="{{AppName}}"> | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css"> | |||
| <link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/icons.woff2" type="font/woff2" crossorigin="anonymous"> | |||
| <link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/outline-icons.woff2" type="font/woff2" crossorigin="anonymous"> | |||
| {{if .RequireSimpleMDE}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | |||
| {{end}} | |||
| {{if .RequireTribute}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css"> | |||
| {{end}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}"> | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}"> | |||
| <noscript> | |||
| <style> | |||
| .dropdown:hover > .menu { display: block; } | |||
| .ui.secondary.menu .dropdown.item > .menu { margin-top: 0; } | |||
| </style> | |||
| </noscript> | |||
| {{if .RequireMinicolors}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css"> | |||
| {{end}} | |||
| <style class="list-search-style"></style> | |||
| {{if .PageIsUserProfile}} | |||
| <meta property="og:title" content="{{.Owner.Name}}" /> | |||
| <meta property="og:type" content="profile" /> | |||
| <meta property="og:image" content="{{.Owner.AvatarLink}}" /> | |||
| <meta property="og:url" content="{{.Owner.HTMLURL}}" /> | |||
| {{if .Owner.Description}} | |||
| <meta property="og:description" content="{{.Owner.Description}}"> | |||
| {{end}} | |||
| {{else if .Repository}} | |||
| {{if .Issue}} | |||
| <meta property="og:title" content="{{.Issue.Title}}" /> | |||
| <meta property="og:url" content="{{.Issue.HTMLURL}}" /> | |||
| {{if .Issue.Content}} | |||
| <meta property="og:description" content="{{.Issue.Content}}" /> | |||
| {{end}} | |||
| {{else}} | |||
| <meta property="og:title" content="{{.Repository.Name}}" /> | |||
| <meta property="og:url" content="{{.Repository.HTMLURL}}" /> | |||
| {{if .Repository.Description}} | |||
| <meta property="og:description" content="{{.Repository.Description}}" /> | |||
| {{end}} | |||
| {{end}} | |||
| <meta property="og:type" content="object" /> | |||
| <meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" /> | |||
| {{else}} | |||
| <meta property="og:title" content="{{AppName}}"> | |||
| <meta property="og:type" content="website" /> | |||
| <meta property="og:image" content="{{StaticUrlPrefix}}/img/gitea-lg.png" /> | |||
| <meta property="og:url" content="{{AppUrl}}" /> | |||
| <meta property="og:description" content="{{MetaDescription}}"> | |||
| {{end}} | |||
| <meta property="og:site_name" content="{{AppName}}" /> | |||
| {{if .IsSigned }} | |||
| {{ if ne .SignedUser.Theme "gitea" }} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}"> | |||
| {{end}} | |||
| {{else if ne DefaultTheme "gitea"}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | |||
| {{end}} | |||
| {{template "custom/header" .}} | |||
| <script> | |||
| var _hmt = _hmt || []; | |||
| (function() { | |||
| var hm = document.createElement("script"); | |||
| hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; | |||
| var s = document.getElementsByTagName("script")[0]; | |||
| s.parentNode.insertBefore(hm, s); | |||
| })(); | |||
| </script> | |||
| <script src="/self/func.js" type="text/javascript"></script> | |||
| </head> | |||
| <body> | |||
| {{template "custom/body_outer_pre" .}} | |||
| <div class="full height"> | |||
| <noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | |||
| {{template "custom/body_inner_pre" .}} | |||
| {{if not .PageIsInstall}} | |||
| <div class="ui top secondary stackable main menu following bar dark"> | |||
| {{template "base/head_navbar_fluid" .}} | |||
| </div><!-- end bar --> | |||
| {{end}} | |||
| {{/* | |||
| </div> | |||
| </body> | |||
| </html> | |||
| */}} | |||
| @@ -181,8 +181,8 @@ | |||
| var _hmt = _hmt || []; | |||
| (function() { | |||
| var hm = document.createElement("script"); | |||
| hm.src = "https://hm.baidu.com/hm.js?7c4ef0a24be6109ab22e63c832ab21cf"; | |||
| var s = document.getElementsByTagName("script")[0]; | |||
| hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; | |||
| var s = document.getElementsByTagName("script")[0]; | |||
| s.parentNode.insertBefore(hm, s); | |||
| })(); | |||
| </script> | |||
| @@ -7,6 +7,14 @@ | |||
| <i class="sidebar icon"></i> | |||
| </div> | |||
| </div> | |||
| <div style="width:1px;background:#606266;height:80%;margin:auto 0.5rem"></div> | |||
| <div class="item brand" style="margin-left: 0.9rem;"> | |||
| <a href="/"> | |||
| <img class="ui mini image" style="height: 1.3rem;" src="{{StaticUrlPrefix}}/img/git-logo.svg"> | |||
| </a> | |||
| </div> | |||
| {{if .IsSigned}} | |||
| <a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a> | |||
| @@ -29,6 +37,9 @@ | |||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||
| {{if .IsAdmin}} | |||
| <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{else if .IsLandingPageHome}} | |||
| @@ -44,6 +55,9 @@ | |||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||
| {{if .IsAdmin}} | |||
| <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{else if .IsLandingPageExplore}} | |||
| @@ -0,0 +1,177 @@ | |||
| <div class="ui fluid container" style = "padding: 0px 20px;" id="navbar"> | |||
| <div class="item brand" style="justify-content: space-between;"> | |||
| <a href="https://openi.org.cn/"> | |||
| <img class="ui mini image" src="{{StaticUrlPrefix}}/img/logo-w.svg"> | |||
| </a> | |||
| <div class="ui basic icon button mobile-only" id="navbar-expand-toggle"> | |||
| <i class="sidebar icon"></i> | |||
| </div> | |||
| </div> | |||
| <div style="width:1px;background:#606266;height:80%;margin:auto 0.5rem"></div> | |||
| <div class="item brand" style="margin-left: 0.9rem;"> | |||
| <a href="/"> | |||
| <img class="ui mini image" style="height: 1.3rem;" src="{{StaticUrlPrefix}}/img/git-logo.svg"> | |||
| </a> | |||
| </div> | |||
| {{if .IsSigned}} | |||
| <a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a> | |||
| {{if not .UnitIssuesGlobalDisabled}} | |||
| <a class="item {{if .PageIsIssues}}active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> | |||
| {{end}} | |||
| {{if not .UnitPullsGlobalDisabled}} | |||
| <a class="item {{if .PageIsPulls}}active{{end}}" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a> | |||
| {{end}} | |||
| {{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}} | |||
| {{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}} | |||
| {{end}} | |||
| <div class="ui dropdown item"> | |||
| {{.i18n.Tr "explore"}} | |||
| <i class="dropdown icon"></i> | |||
| <div class="menu"> | |||
| <a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||
| {{if .IsAdmin}} | |||
| <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{else if .IsLandingPageHome}} | |||
| <a class="item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/dashboard">{{.i18n.Tr "home"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a> | |||
| <div class="ui dropdown item"> | |||
| {{.i18n.Tr "explore"}} | |||
| <i class="dropdown icon"></i> | |||
| <div class="menu"> | |||
| <a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||
| {{if .IsAdmin}} | |||
| <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{else if .IsLandingPageExplore}} | |||
| <a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "home"}}</a> | |||
| {{else if .IsLandingPageOrganizations}} | |||
| <a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "home"}}</a> | |||
| {{end}} | |||
| {{template "custom/extra_links" .}} | |||
| {{/* | |||
| <div class="item"> | |||
| <div class="ui icon input"> | |||
| <input class="searchbox" type="text" placeholder="{{.i18n.Tr "search_project"}}"> | |||
| <i class="search icon"></i> | |||
| </div> | |||
| </div> | |||
| */}} | |||
| {{if .IsSigned}} | |||
| <div class="right stackable menu"> | |||
| <a href="{{AppSubUrl}}/notifications" class="item poping up" data-content='{{.i18n.Tr "notifications"}}' data-variation="tiny inverted"> | |||
| <span class="text"> | |||
| <span class="fitted">{{svg "octicon-bell" 16}}</span> | |||
| <span class="sr-mobile-only">{{.i18n.Tr "notifications"}}</span> | |||
| {{$notificationUnreadCount := 0}} | |||
| {{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}} | |||
| <span class="ui red label {{if not $notificationUnreadCount}}hidden{{end}} notification_count"> | |||
| {{$notificationUnreadCount}} | |||
| </span> | |||
| </span> | |||
| </a> | |||
| <div class="ui dropdown jump item poping up" data-content="{{.i18n.Tr "create_new"}}" data-variation="tiny inverted"> | |||
| <span class="text"> | |||
| <span class="fitted">{{svg "octicon-plus" 16}}</span> | |||
| <span class="sr-mobile-only">{{.i18n.Tr "create_new"}}</span> | |||
| <span class="fitted not-mobile">{{svg "octicon-triangle-down" 16}}</span> | |||
| </span> | |||
| <div class="menu"> | |||
| <a class="item" href="{{AppSubUrl}}/repo/create"> | |||
| <span class="fitted">{{svg "octicon-plus" 16}}</span> {{.i18n.Tr "new_repo"}} | |||
| </a> | |||
| <a class="item" href="{{AppSubUrl}}/repo/migrate"> | |||
| <span class="fitted">{{svg "octicon-repo-clone" 16}}</span> {{.i18n.Tr "new_migrate"}} | |||
| </a> | |||
| {{if .SignedUser.CanCreateOrganization}} | |||
| <a class="item" href="{{AppSubUrl}}/org/create"> | |||
| <span class="fitted">{{svg "octicon-organization" 16}}</span> {{.i18n.Tr "new_org"}} | |||
| </a> | |||
| {{end}} | |||
| </div><!-- end content create new menu --> | |||
| </div><!-- end dropdown menu create new --> | |||
| <div class="ui dropdown jump item poping up" tabindex="-1" data-content="{{.i18n.Tr "user_profile_and_more"}}" data-variation="tiny inverted"> | |||
| <span class="text"> | |||
| <img class="ui tiny avatar image" width="24" height="24" src="{{.SignedUser.RelAvatarLink}}"> | |||
| <span class="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span> | |||
| <span class="mobile-only">{{.SignedUser.Name}}</span> | |||
| <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down" 16}}</span> | |||
| </span> | |||
| <div class="menu user-menu" tabindex="-1"> | |||
| <div class="ui header"> | |||
| {{.i18n.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong> | |||
| </div> | |||
| <div class="divider"></div> | |||
| <a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}"> | |||
| {{svg "octicon-person" 16}} | |||
| {{.i18n.Tr "your_profile"}}<!-- Your profile --> | |||
| </a> | |||
| <a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}?tab=stars"> | |||
| {{svg "octicon-star" 16}} | |||
| {{.i18n.Tr "your_starred"}} | |||
| </a> | |||
| <a class="{{if .PageIsUserSettings}}active{{end}} item" href="{{AppSubUrl}}/user/settings"> | |||
| {{svg "octicon-settings" 16}} | |||
| {{.i18n.Tr "your_settings"}}<!-- Your settings --> | |||
| </a> | |||
| <!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io"> | |||
| {{svg "octicon-question" 16}} | |||
| {{.i18n.Tr "help"}}<!-- Help --> | |||
| </a--> | |||
| {{if .IsAdmin}} | |||
| <div class="divider"></div> | |||
| <a class="{{if .PageIsAdmin}}active{{end}} item" href="{{AppSubUrl}}/admin"> | |||
| <i class="icon settings"></i> | |||
| {{.i18n.Tr "admin_panel"}}<!-- Admin Panel --> | |||
| </a> | |||
| {{end}} | |||
| <div class="divider"></div> | |||
| <a class="item link-action" href data-url="{{AppSubUrl}}/user/logout" data-redirect="{{AppSubUrl}}/"> | |||
| {{svg "octicon-sign-out" 16}} | |||
| {{.i18n.Tr "sign_out"}}<!-- Sign Out --> | |||
| </a> | |||
| </div><!-- end content avatar menu --> | |||
| </div><!-- end dropdown avatar menu --> | |||
| </div><!-- end signed user right menu --> | |||
| {{else}} | |||
| <!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a--> | |||
| <div class="right stackable menu"> | |||
| {{if .ShowRegistrationButton}} | |||
| <a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up"> | |||
| {{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
| </a> | |||
| {{end}} | |||
| <a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login"> | |||
| {{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
| </a> | |||
| </div><!-- end anonymous right menu --> | |||
| {{end}} | |||
| </div> | |||
| @@ -29,6 +29,9 @@ | |||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||
| {{if .IsAdmin}} | |||
| <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{else if .IsLandingPageHome}} | |||
| @@ -44,6 +47,9 @@ | |||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | |||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||
| {{if .IsAdmin}} | |||
| <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{else if .IsLandingPageExplore}} | |||
| @@ -0,0 +1,15 @@ | |||
| {{template "base/head_fluid" .}} | |||
| <div id="data_analysis" style="height: 100%;"> | |||
| </div> | |||
| {{template "base/footer_fluid" .}} | |||
| <style> | |||
| .full.height { | |||
| /* flex-grow: 1; */ | |||
| padding-bottom: 53px; | |||
| } | |||
| </style> | |||
| @@ -7,7 +7,11 @@ | |||
| </div> | |||
| </h1> | |||
| <p class="am-lh-18">免费私有代码仓库,免费计算资源,大容量数据存储,<br>多类型硬件环境(GPU、NPU),AI开发流水线(开发-调试-训练-迭代)</p> | |||
| <a class="circular ui secondary button" href="{{AppSubUrl}}/user/sign_up">立即使用 <i class="right arrow icon"></i></a> | |||
| {{if .IsSigned}} | |||
| <a class="circular ui secondary button" href="{{AppSubUrl}}/dashboard">立即使用 <i class="right arrow icon"></i></a> | |||
| {{else}} | |||
| <a class="circular ui secondary button" href="{{AppSubUrl}}/user/login">立即使用 <i class="right arrow icon"></i></a> | |||
| {{end}} | |||
| <div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div> | |||
| </div> | |||
| </div><!-- end segment --> | |||
| @@ -33,7 +37,11 @@ | |||
| <p class="am-lh-18">在这里为你和你的团队创建项目,基于Git工具,提交记录或者回滚代码修改。<br> | |||
| 不论是公开或者私有仓库,都可免费使用所有功能。<br> | |||
| 尽情将你喜欢的代码都放在这里,仓库数量、存储容量不受限</p> | |||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/sign_up">立即使用</a> | |||
| {{if .IsSigned}} | |||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a> | |||
| {{else}} | |||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a> | |||
| {{end}} | |||
| </div> | |||
| <div class="ten wide column computer only i-code-pic am-pt-30"> | |||
| <img class="ui fluid rounded image am-shadow-2 am-mt-10" src="/img/i-code-pic.jpg" style="position: absolute;"> | |||
| @@ -184,7 +192,12 @@ | |||
| 开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等<br> | |||
| 如果您的模型需要更多的计算资源,也可以单独申请<br> | |||
| </p> | |||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/sign_up">马上使用</a> <a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||
| {{if .IsSigned}} | |||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||
| {{else}} | |||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -38,11 +38,12 @@ | |||
| <div class="ui sixteen wide mobile six wide tablet five wide computer column"> | |||
| <h4 class="ui top attached header"> | |||
| <strong>{{.i18n.Tr "org.people"}}</strong> | |||
| {{if .IsOrganizationMember}} | |||
| <div class="ui right"> | |||
| <a class="text grey" href="{{.OrgLink}}/members">{{.Org.NumMembers}} {{svg "octicon-chevron-right" 16}}</a> | |||
| </div> | |||
| {{end}} | |||
| <div class="ui right"> | |||
| <a class="text grey" href="{{.OrgLink}}/members">{{.MembersTotal}} {{svg "octicon-chevron-right" 16}}</a> | |||
| </div> | |||
| <!-- {{if .IsOrganizationMember}} --> | |||
| <!-- {{end}} --> | |||
| </h4> | |||
| <div class="ui attached segment members"> | |||
| {{$isMember := .IsOrganizationMember}} | |||
| @@ -3,10 +3,10 @@ | |||
| <a class="{{if $.PageIsOrgHome}}active{{end}} item" href="{{.HomeLink}}"> | |||
| {{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | |||
| </a> | |||
| <a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||
| {{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||
| </a> | |||
| {{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}} | |||
| <a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||
| {{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||
| </a> | |||
| <a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams"> | |||
| {{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | |||
| </a> | |||
| @@ -23,10 +23,10 @@ | |||
| {{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | |||
| </a> | |||
| {{end}} | |||
| <a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||
| {{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||
| </a> | |||
| {{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}} | |||
| <a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||
| {{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||
| </a> | |||
| <a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams"> | |||
| {{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | |||
| </a> | |||
| @@ -2,8 +2,18 @@ | |||
| <div class="repository commits"> | |||
| {{template "repo/header" .}} | |||
| <div class="ui container"> | |||
| <h2 class="ui header">{{.DateFrom}} - {{.DateUntil}} | |||
| <div class="ui right"> | |||
| <div class="ui three column stackable grid" style="align-items: center;"> | |||
| <div class="column"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="active section" href="{{.RepoLink}}/activity">{{.i18n.Tr "repo.activity"}}</div> | |||
| </div> | |||
| </div> | |||
| <div class="column center aligned" style="font-weight: 800;"> | |||
| {{.DateFrom}} - {{.DateUntil}} | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <!-- Period --> | |||
| <div class="ui floating dropdown jump filter"> | |||
| <div class="ui basic compact button"> | |||
| @@ -23,7 +33,7 @@ | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </h2> | |||
| </div> | |||
| <div class="ui divider"></div> | |||
| {{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}} | |||
| @@ -239,8 +239,8 @@ | |||
| <div class="column"> | |||
| <div class="ui blue small menu compact selectcloudbrain"> | |||
| <a class="active item">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||
| <!-- <a class="item" href="{{.RepoLink}}/modelarts">训练任务</a> --> | |||
| <a class="active item" href="{{.RepoLink}}/cloudbrain">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||
| <a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> | |||
| </div> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| @@ -307,9 +307,9 @@ | |||
| <!-- 任务名 --> | |||
| <div class="five wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted text_over" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
| <span class="fitted text_over" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
| </a> | |||
| </div> | |||
| @@ -380,7 +380,7 @@ | |||
| {{end}} | |||
| </form> | |||
| </div> | |||
| <div class="ui compact buttons" style="margin-right:10px;"> | |||
| <div class="ui compact buttons"> | |||
| <!-- 模型下载 --> | |||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | |||
| {{$.i18n.Tr "repo.download"}} | |||
| @@ -399,11 +399,11 @@ | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a id="model-delete-{{.JobID}}" class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a id="model-delete-{{.JobID}}" class="ui basic button {{if not .CanDel}}disabled {{else}} blue {{end}}" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| {{$.i18n.Tr "repo.delete"}} | |||
| </a> | |||
| {{else}} | |||
| <a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a class="ui basic blue button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| {{$.i18n.Tr "repo.delete"}} | |||
| </a> | |||
| {{end}} | |||
| @@ -427,12 +427,12 @@ | |||
| <div class="inline required field dis"> | |||
| <label>镜像标签:</label> | |||
| <input name="tag" id="image_tag" tabindex="3" autofocus required maxlength="255" style="width:75%"> | |||
| <input name="tag" id="image_tag" tabindex="3" autofocus required maxlength="254" style="width:75%"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label class="label_after">镜像描述:</label> | |||
| <textarea name="description" rows="8" style="width:75%;margin-left: 0.2em;"></textarea> | |||
| <textarea name="description" maxlength="254" rows="8" style="width:75%;margin-left: 0.2em;"></textarea> | |||
| </div> | |||
| <div class="ui divider"></div> | |||
| @@ -131,7 +131,7 @@ | |||
| <div class="ui attached segment"> | |||
| <div class="inline required field"> | |||
| <label>任务名称</label> | |||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="254"> | |||
| </div> | |||
| <div class="inline required field" style="{{if ((.is_benchmark_enabled) or (.is_snn4imagenet_enabled) or (.is_brainscore_enabled))}}display:block;{{else}}display:none;{{end}}"> | |||
| @@ -165,7 +165,7 @@ | |||
| </div> | |||
| <input id="store_category" type="hidden" name="get_benchmark_category"> | |||
| <div class="inline required field cloudbrain_benchmark"> | |||
| <div class="inline required field"> | |||
| <label>GPU类型</label> | |||
| <select id="cloudbrain_gpu_type" class="ui search dropdown" placeholder="选择GPU类型" style='width:385px' name="gpu_type"> | |||
| {{range .gpu_types}} | |||
| @@ -176,7 +176,7 @@ | |||
| <div class="inline required field"> | |||
| <label>镜像</label> | |||
| <input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image" required autofocus maxlength="255"> | |||
| <input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image" required autofocus maxlength="254"> | |||
| <datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image"> | |||
| {{range .images}} | |||
| <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | |||
| @@ -208,27 +208,27 @@ | |||
| <div class="inline required field"> | |||
| <label>数据集存放路径</label> | |||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>模型存放路径</label> | |||
| <input name="model_path" id="cloudbrain_model_path" value="{{.model_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="model_path" id="cloudbrain_model_path" value="{{.model_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>代码存放路径</label> | |||
| <input name="code_path" id="cloudbrain_code_path" value="{{.code_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="code_path" id="cloudbrain_code_path" value="{{.code_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field cloudbrain_benchmark"> | |||
| <label>benchmark脚本存放路径</label> | |||
| <input name="benchmark_path" id="cloudbrain_benchmark_path" value="{{.benchmark_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="benchmark_path" id="cloudbrain_benchmark_path" value="{{.benchmark_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field cloudbrain_snn4imagenet"> | |||
| <label>snn4imagenet脚本存放路径</label> | |||
| <input name="snn4imagenet_path" id="cloudbrain_snn4imagenet_path" value="{{.snn4imagenet_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="snn4imagenet_path" id="cloudbrain_snn4imagenet_path" value="{{.snn4imagenet_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field cloudbrain_brainscore"> | |||
| <label>brainscore脚本存放路径</label> | |||
| <input name="brainscore_path" id="cloudbrain_brainscore_path" value="{{.brainscore_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="brainscore_path" id="cloudbrain_brainscore_path" value="{{.brainscore_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field" hidden> | |||
| <label>启动命令</label> | |||
| @@ -6,7 +6,19 @@ | |||
| {{template "base/alert" .}} | |||
| <h4 class="ui header" id="vertical-segment"> | |||
| <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/cloudbrain"> | |||
| {{.i18n.Tr "repo.cloudbrain"}} | |||
| </a> | |||
| <div class="divider"> / </div> | |||
| <a class="section" href="{{.RepoLink}}/cloudbrain"> | |||
| {{$.i18n.Tr "repo.modelarts.notebook"}} | |||
| </a> | |||
| <div class="divider"> / </div> | |||
| {{with .task}} | |||
| <div class="active section">{{.JobName}}</div> | |||
| {{end}} | |||
| </div> | |||
| </h4> | |||
| <div> | |||
| <div class="ui yellow segment"> | |||
| @@ -0,0 +1,9 @@ | |||
| {{template "base/head" .}} | |||
| <div class="repository watchers"> | |||
| {{template "repo/header" .}} | |||
| <div class="ui container" id="Contributors"> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -54,7 +54,7 @@ | |||
| </div> | |||
| <div class="inline field {{if .Err_Description}}error{{end}}"> | |||
| <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> | |||
| <textarea id="description" name="description">{{.description}}</textarea> | |||
| <textarea id="description" name="description" maxlength="254">{{.description}}</textarea> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label>{{.i18n.Tr "repo.template"}}</label> | |||
| @@ -57,7 +57,7 @@ | |||
| <div class="ui grid form segment success {{if not .Error}}hide{{end}}" id="dataset-content-edit"> | |||
| <label class="d-block">{{.i18n.Tr "dataset.title"}}</label> | |||
| <div class="sixteen wide column"> | |||
| <input name="title" placeholder='{{.i18n.Tr "dataset.title"}}' value="{{.dataset.Title}}" autofocus required maxlength="255"> | |||
| <input name="title" placeholder='{{.i18n.Tr "dataset.title"}}' value="{{.dataset.Title}}" autofocus required maxlength="254"> | |||
| </div> | |||
| <label class="d-block">{{.i18n.Tr "dataset.description"}}</label> | |||
| <div class="sixteen wide column"> | |||
| @@ -92,16 +92,27 @@ | |||
| {{if not .Repository.IsBeingCreated}} | |||
| <div class="ui tabular stackable menu navbar"> | |||
| {{if .Permission.CanRead $.UnitTypeCode}} | |||
| <a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> | |||
| {{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} | |||
| <div class="dropdown-menu"> | |||
| <a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> | |||
| <span>{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} <i class="dropdown icon"></i></span> | |||
| </a> | |||
| {{end}} | |||
| <div class="dropdown-content"> | |||
| <a style="border: none;" class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> | |||
| {{svg "octicon-tag" 16}} {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .NumReleases}}gray{{else}}blue{{end}} small label">{{.NumReleases}}</span> | |||
| </a> | |||
| <a style="border: none;" class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}> | |||
| {{svg "octicon-book" 16}} {{.i18n.Tr "repo.wiki"}} | |||
| </a> | |||
| <a style="border: none;" class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity"> | |||
| {{svg "octicon-pulse" 16}} {{.i18n.Tr "repo.activity"}} | |||
| </a> | |||
| {{if .Permission.CanRead $.UnitTypeDatasets}} | |||
| <a class="{{if .PageIsDataset}}active{{end}} item" href="{{.RepoLink}}/datasets?type=0"> | |||
| {{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {{end}} | |||
| {{if .Permission.CanRead $.UnitTypeIssues}} | |||
| <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | |||
| @@ -109,11 +120,11 @@ | |||
| </a> | |||
| {{end}} | |||
| {{if .Permission.CanRead $.UnitTypeExternalTracker}} | |||
| <!-- {{if .Permission.CanRead $.UnitTypeExternalTracker}} | |||
| <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer"> | |||
| {{svg "octicon-link-external" 16}} {{.i18n.Tr "repo.issues"}} </span> | |||
| </a> | |||
| {{end}} | |||
| {{end}} --> | |||
| {{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}} | |||
| <a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | |||
| @@ -121,35 +132,22 @@ | |||
| </a> | |||
| {{end}} | |||
| {{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }} | |||
| <a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases"> | |||
| {{svg "octicon-tag" 16}} {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .NumReleases}}gray{{else}}blue{{end}} small label">{{.NumReleases}}</span> | |||
| {{if .Permission.CanRead $.UnitTypeDatasets}} | |||
| <a class="{{if .PageIsDataset}}active{{end}} item" href="{{.RepoLink}}/datasets?type=0"> | |||
| {{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}} | |||
| </a> | |||
| {{end}} | |||
| {{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}} | |||
| <a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}> | |||
| {{svg "octicon-book" 16}} {{.i18n.Tr "repo.wiki"}} | |||
| </a> | |||
| {{end}} | |||
| {{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}} | |||
| <a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity"> | |||
| {{svg "octicon-pulse" 16}} {{.i18n.Tr "repo.activity"}} | |||
| </a> | |||
| {{end}} | |||
| {{if .Permission.CanRead $.UnitTypeCloudBrain}} | |||
| <a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/cloudbrain"> | |||
| {{svg "octicon-server" 16}} {{.i18n.Tr "repo.cloudbrain"}} | |||
| <span>{{svg "octicon-server" 16}} {{.i18n.Tr "repo.cloudbrain"}}<i class="question circle icon link cloudbrain-question" data-content={{.i18n.Tr "repo.cloudbrain_helper"}} data-position="top center" data-variation="mini"></i></span> | |||
| </a> | |||
| {{end}} | |||
| {{if .IsSigned}} | |||
| <!-- {{if .IsSigned}} | |||
| <a class="{{if .PageIsBlockChain}}active{{end}} item " href="{{.RepoLink}}/blockchain"> | |||
| {{svg "octicon-law" 16}} | |||
| {{.i18n.Tr "repo.balance"}} | |||
| </a> | |||
| {{end}} | |||
| {{end}} --> | |||
| {{template "custom/extra_tabs" .}} | |||
| @@ -243,4 +241,9 @@ | |||
| window.location.href = repolink + "/datasets?type=" + checked_radio | |||
| }) | |||
| }) | |||
| $('.question.circle.icon').hover(function(){ | |||
| $(this).popup('show') | |||
| $('.ui.popup.mini.top.center').css({"border-color":'rgba(50, 145, 248, 100)',"color":"rgba(3, 102, 214, 100)","border-radius":"5px","border-shadow":"none"}) | |||
| }); | |||
| </script> | |||
| @@ -4,7 +4,7 @@ | |||
| font-size: 1.0em; | |||
| margin-bottom: 1.0rem; | |||
| } | |||
| #contributorInfo > a:nth-child(n+25){ | |||
| #contributorInfo > a:nth-child(n+26){ | |||
| display:none; | |||
| } | |||
| #contributorInfo > a{ | |||
| @@ -329,9 +329,15 @@ | |||
| <div> | |||
| <h4 class="ui header"> | |||
| {{$lenCon := len .ContributorInfo}} | |||
| {{if lt $lenCon 25 }} | |||
| <strong>贡献者 ({{len .ContributorInfo}})</strong> | |||
| {{else}} | |||
| <strong>贡献者 ({{len .ContributorInfo}}+)</strong> | |||
| {{end}} | |||
| <div class="ui right"> | |||
| <a class="membersmore text grey" href="javascript:;">全部 {{svg "octicon-chevron-right" 16}}</a> | |||
| <a class="membersmore text grey" href="{{.RepoLink}}/contributors">全部 {{svg "octicon-chevron-right" 16}}</a> | |||
| </div> | |||
| </h4> | |||
| <div class="ui members" id="contributorInfo"> | |||
| @@ -353,10 +359,10 @@ | |||
| </div> | |||
| <script type="text/javascript"> | |||
| $(document).ready(function(){ | |||
| $(".membersmore").click(function(){ | |||
| $("#contributorInfo > a:nth-child(n+25)").show(); | |||
| }); | |||
| }); | |||
| // $(document).ready(function(){ | |||
| // $(".membersmore").click(function(){ | |||
| // $("#contributorInfo > a:nth-child(n+25)").show(); | |||
| // }); | |||
| // }); | |||
| </script> | |||
| {{template "base/footer" .}} | |||
| @@ -4,7 +4,7 @@ | |||
| <div class="ui container"> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.Title | RenderEmoji}}</div> | |||
| @@ -4,7 +4,7 @@ | |||
| <div class="ui container"> | |||
| <div class="ui three column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | |||
| <div class="divider"> / </div> | |||
| <a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> | |||
| @@ -4,7 +4,7 @@ | |||
| <div class="ui container"> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | |||
| <div class="divider"> / </div> | |||
| <a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> | |||
| @@ -4,7 +4,7 @@ | |||
| <div class="ui container"> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.Title | RenderEmoji}}</div> | |||
| @@ -4,7 +4,7 @@ | |||
| <div class="ui container"> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.i18n.Tr "repo.issues.new"}}</div> | |||
| @@ -15,7 +15,7 @@ | |||
| <div class="ui segment content"> | |||
| <div class="field"> | |||
| <!-- --> | |||
| <input name="title" id="issue_title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" tabindex="3" autofocus required maxlength="255"> | |||
| <input name="title" id="issue_title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" tabindex="3" autofocus required maxlength="254"> | |||
| {{if .PageIsComparePull}} | |||
| <div class="title_wip_desc">{{.i18n.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> | |||
| {{end}} | |||
| @@ -5,13 +5,13 @@ | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| {{if .PageIsIssueList}} | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | |||
| </div> | |||
| {{else}} | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | |||
| @@ -3,7 +3,7 @@ | |||
| <h1 class="twelve wide column"> | |||
| <span class="index">#{{.Issue.Index}}</span> <span id="issue-title">{{RenderEmoji .Issue.Title}}</span> | |||
| <div id="edit-title-input" class="ui input" style="display: none"> | |||
| <input value="{{.Issue.Title}}" maxlength="255"> | |||
| <input value="{{.Issue.Title}}" maxlength="254"> | |||
| </div> | |||
| </h1> | |||
| {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} | |||
| @@ -122,7 +122,7 @@ | |||
| </div> | |||
| <div class="inline field {{if .Err_Description}}error{{end}}"> | |||
| <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> | |||
| <textarea id="description" name="description">{{.description}}</textarea> | |||
| <textarea id="description" name="description" maxlength="254">{{.description}}</textarea> | |||
| </div> | |||
| <div class="inline field"> | |||
| @@ -1,485 +0,0 @@ | |||
| <!-- 头部导航栏 --> | |||
| {{template "base/head" .}} | |||
| <style> | |||
| .selectcloudbrain .active.item{ | |||
| color: #0087f5 !important; | |||
| border: 1px solid #0087f5; | |||
| margin: -1px; | |||
| background: #FFF !important; | |||
| } | |||
| #deletemodel { | |||
| width: 100%; | |||
| height: 100%; | |||
| } | |||
| /* 弹窗 */ | |||
| #mask { | |||
| position: fixed; | |||
| top: 0px; | |||
| left: 0px; | |||
| right: 0px; | |||
| bottom: 0px; | |||
| filter: alpha(opacity=60); | |||
| background-color: #777; | |||
| z-index: 1000; | |||
| display: none; | |||
| opacity: 0.8; | |||
| -moz-opacity: 0.5; | |||
| padding-top: 100px; | |||
| color: #000000 | |||
| } | |||
| #loadingPage { | |||
| margin: 200px auto; | |||
| width: 50px; | |||
| height: 40px; | |||
| text-align: center; | |||
| font-size: 10px; | |||
| display: block; | |||
| } | |||
| #loadingPage>div { | |||
| background-color: green; | |||
| height: 100%; | |||
| width: 6px; | |||
| display: inline-block; | |||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| } | |||
| #loadingPage .rect2 { | |||
| -webkit-animation-delay: -1.1s; | |||
| animation-delay: -1.1s; | |||
| } | |||
| #loadingPage .rect3 { | |||
| -webkit-animation-delay: -1.0s; | |||
| animation-delay: -1.0s; | |||
| } | |||
| #loadingPage .rect4 { | |||
| -webkit-animation-delay: -0.9s; | |||
| animation-delay: -0.9s; | |||
| } | |||
| #loadingPage .rect5 { | |||
| -webkit-animation-delay: -0.8s; | |||
| animation-delay: -0.8s; | |||
| } | |||
| @-webkit-keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| -webkit-transform: scaleY(0.4) | |||
| } | |||
| 20% { | |||
| -webkit-transform: scaleY(1.0) | |||
| } | |||
| } | |||
| @keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| transform: scaleY(0.4); | |||
| -webkit-transform: scaleY(0.4); | |||
| } | |||
| 20% { | |||
| transform: scaleY(1.0); | |||
| -webkit-transform: scaleY(1.0); | |||
| } | |||
| } | |||
| /* 消息框 */ | |||
| .alert { | |||
| display: none; | |||
| position: fixed; | |||
| width: 100%; | |||
| z-index: 1001; | |||
| padding: 15px; | |||
| border: 1px solid transparent; | |||
| border-radius: 4px; | |||
| text-align: center; | |||
| font-weight: bold; | |||
| } | |||
| .alert-success { | |||
| color: #3c763d; | |||
| background-color: #dff0d8; | |||
| border-color: #d6e9c6; | |||
| } | |||
| .alert-info { | |||
| color: #31708f; | |||
| background-color: #d9edf7; | |||
| border-color: #bce8f1; | |||
| } | |||
| .alert-warning { | |||
| color: #8a6d3b; | |||
| background-color: #fcf8e3; | |||
| border-color: #faebcc; | |||
| } | |||
| .alert-danger { | |||
| color: #a94442; | |||
| background-color: #f2dede; | |||
| border-color: #ebccd1; | |||
| } | |||
| .pusher { | |||
| width: calc(100% - 260px); | |||
| box-sizing: border-box; | |||
| } | |||
| /* 弹窗 (background) */ | |||
| #imageModal { | |||
| display: none; | |||
| position: fixed; | |||
| z-index: 1; | |||
| left: 0; | |||
| top: 0; | |||
| width: 100%; | |||
| height: 100%; | |||
| overflow: auto; | |||
| background-color: rgb(0, 0, 0); | |||
| background-color: rgba(0, 0, 0, 0.4); | |||
| } | |||
| /* 弹窗内容 */ | |||
| .modal-content { | |||
| background-color: #fefefe; | |||
| margin: 15% auto; | |||
| padding: 20px; | |||
| border: 1px solid #888; | |||
| width: 30%; | |||
| } | |||
| /* 关闭按钮 */ | |||
| .close { | |||
| color: #aaa; | |||
| float: right; | |||
| font-size: 28px; | |||
| font-weight: bold; | |||
| } | |||
| .close:hover, | |||
| .close:focus { | |||
| color: black; | |||
| text-decoration: none; | |||
| cursor: pointer; | |||
| } | |||
| .dis { | |||
| margin-bottom: 20px; | |||
| } | |||
| .disabled { | |||
| cursor: pointer; | |||
| pointer-events: none; | |||
| } | |||
| </style> | |||
| <!-- 弹窗 --> | |||
| <div id="mask"> | |||
| <div id="loadingPage"> | |||
| <div class="rect1"></div> | |||
| <div class="rect2"></div> | |||
| <div class="rect3"></div> | |||
| <div class="rect4"></div> | |||
| <div class="rect5"></div> | |||
| </div> | |||
| </div> | |||
| <!-- 提示框 --> | |||
| <div class="alert"></div> | |||
| <div class="repository release dataset-list view"> | |||
| {{template "repo/header" .}} | |||
| <!-- 列表容器 --> | |||
| <div class="ui container"> | |||
| <!-- 中间云脑和新建任务按钮 --> | |||
| <div class="ui two column stackable grid "> | |||
| <div class="column"> | |||
| <div class="ui blue small menu compact selectcloudbrain"> | |||
| <a class="active item" href="{{.RepoLink}}/modelarts/notebook">调试任务</a> | |||
| <a class="item" href="{{.RepoLink}}/modelarts/train-job">训练任务</a> | |||
| </div> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
| {{svg "octicon-server" 16}} | |||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||
| <i class="dropdown icon"></i> | |||
| <div class="menu"> | |||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||
| <a class="item" href="{{.RepoLink}}/modelarts" data-value="22">Ascend NPU</a> | |||
| </div> | |||
| </div> | |||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/create">新建调试任务</a> | |||
| </div> | |||
| </div> | |||
| <!-- 中下列表展示区 --> | |||
| <div class="ui grid"> | |||
| <div class="row"> | |||
| <div class="ui sixteen wide column"> | |||
| <!-- 排序区 --> | |||
| <!-- <div class="ui sixteen wide column"> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column"> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <div class="ui right dropdown type jump item"> | |||
| <span class="text"> | |||
| {{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i> | |||
| </span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> --> | |||
| <!-- 任务展示 --> | |||
| <div class="dataset list"> | |||
| <!-- 表头 --> | |||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||
| <div class="row"> | |||
| <div class="five wide column"> | |||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||
| </div> | |||
| <div class="three wide column"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span> | |||
| </div> | |||
| <div class="one wide column"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||
| </div> | |||
| <div class="seven wide column text center"> | |||
| <span style="margin-left: 10rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{range .Tasks}} | |||
| <div class="ui grid stackable item"> | |||
| <div class="row"> | |||
| <!-- 任务名 --> | |||
| <div class="five wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||
| </a> | |||
| </div> | |||
| <div class="three wide column"> | |||
| <!--任务状态 --> | |||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| {{.Status}} | |||
| </span> --> | |||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| <span><i style="vertical-align: middle;" class="{{.Status}}"></i><span style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| </span> | |||
| <!-- 任务创建时间 --> | |||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||
| </div> | |||
| <div class="one wide column"> | |||
| {{if .User.Name}} | |||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||
| {{else}} | |||
| <a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||
| {{end}} | |||
| </div> | |||
| <div class="seven wide column text right"> | |||
| <div class="ui compact buttons" style="margin-right:10px;"> | |||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}"> | |||
| 查看 | |||
| </a> | |||
| <a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||
| 调试 | |||
| </a> | |||
| <form id="stopForm-{{.JobID}}" action="{{if ne .Status "RUNNING"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/stop{{end}}" method="post" style="margin-left:-1px;"> | |||
| {{$.CsrfTokenHtml}} | |||
| <a class="ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
| 停止 | |||
| </a> | |||
| </form> | |||
| </div> | |||
| <!-- 删除任务 --> | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| <a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| 删除 | |||
| </a> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{end}} {{template "base/paginate" .}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- 确认模态框 --> | |||
| <div id="deletemodel"> | |||
| <div class="ui basic modal"> | |||
| <div class="ui icon header"> | |||
| <i class="trash icon"></i> 删除任务 | |||
| </div> | |||
| <div class="content"> | |||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||
| </div> | |||
| <div class="actions"> | |||
| <div class="ui red basic inverted cancel button"> | |||
| <i class="remove icon"></i> 取消操作 | |||
| </div> | |||
| <div class="ui green basic inverted ok button"> | |||
| <i class="checkmark icon"></i> 确定操作 | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| // 调试和评分新开窗口 | |||
| function stop(obj) { | |||
| if (obj.style.color != "rgb(204, 204, 204)") { | |||
| obj.target = '_blank' | |||
| } else { | |||
| return | |||
| } | |||
| } | |||
| // 删除时用户确认 | |||
| function assertDelete(obj) { | |||
| if (obj.style.color == "rgb(204, 204, 204)") { | |||
| return | |||
| } else { | |||
| var delId = obj.parentNode.id | |||
| flag = 1; | |||
| $('.ui.basic.modal') | |||
| .modal({ | |||
| onDeny: function() { | |||
| flag = false | |||
| }, | |||
| onApprove: function() { | |||
| document.getElementById(delId).submit() | |||
| flag = true | |||
| }, | |||
| onHidden: function() { | |||
| if (flag == false) { | |||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
| } | |||
| } | |||
| }) | |||
| .modal('show') | |||
| } | |||
| } | |||
| // 加载任务状态 | |||
| var timeid = window.setInterval(loadJobStatus, 15000); | |||
| $(document).ready(loadJobStatus); | |||
| function loadJobStatus() { | |||
| $(".job-status").each((index, job) => { | |||
| const jobID = job.dataset.jobid; | |||
| const repoPath = job.dataset.repopath; | |||
| if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'START_FAILED' || job.textContent.trim() == 'CREATE_FAILED') { | |||
| return | |||
| } | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/${jobID}`, (data) => { | |||
| const jobID = data.JobID | |||
| const status = data.JobStatus | |||
| if (status != job.textContent.trim()) { | |||
| //$('#' + jobID).text(status) | |||
| //if (status == 'STOPPED') { | |||
| window.location.reload() | |||
| //} | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| }); | |||
| }; | |||
| // 获取弹窗 | |||
| var modal = document.getElementById('imageModal'); | |||
| // 打开弹窗的按钮对象 | |||
| var btns = document.getElementsByClassName("imageBtn"); | |||
| // 获取 <span> 元素,用于关闭弹窗 | |||
| var spans = document.getElementsByClassName('close'); | |||
| // 点击按钮打开弹窗 | |||
| for (i = 0; i < btns.length; i++) { | |||
| btns[i].onclick = function() { | |||
| modal.style.display = "block"; | |||
| } | |||
| } | |||
| // 点击 <span> (x), 关闭弹窗 | |||
| for (i = 0; i < spans.length; i++) { | |||
| spans[i].onclick = function() { | |||
| modal.style.display = "none"; | |||
| } | |||
| } | |||
| // 在用户点击其他地方时,关闭弹窗 | |||
| window.onclick = function(event) { | |||
| if (event.target == modal) { | |||
| modal.style.display = "none"; | |||
| } | |||
| } | |||
| // 显示弹窗,弹出相应的信息 | |||
| function showmask() { | |||
| $('#imageModal').css('display', 'none') | |||
| $('#mask').css('display', 'block') | |||
| $("iframe[name=iframeContent]").on("load", function() { | |||
| var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||
| var json1 = JSON.parse(responseText) | |||
| $('#mask').css('display', 'none') | |||
| parent.location.href | |||
| if (json1.result_code === "0") { | |||
| $('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||
| } else { | |||
| $('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||
| } | |||
| }) | |||
| } | |||
| </script> | |||
| @@ -1,240 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| <style> | |||
| /* 遮罩层css效果图 */ | |||
| #mask { | |||
| position: fixed; | |||
| top: 0px; | |||
| left: 0px; | |||
| right: 0px; | |||
| bottom: 0px; | |||
| filter: alpha(opacity=60); | |||
| background-color: #777; | |||
| z-index: 1000; | |||
| display: none; | |||
| opacity: 0.8; | |||
| -moz-opacity: 0.5; | |||
| padding-top: 100px; | |||
| color: #000000 | |||
| } | |||
| /* 加载圈css效果图 */ | |||
| #loadingPage { | |||
| margin: 200px auto; | |||
| width: 50px; | |||
| height: 40px; | |||
| text-align: center; | |||
| font-size: 10px; | |||
| display: block; | |||
| } | |||
| #loadingPage>div { | |||
| background-color: green; | |||
| height: 100%; | |||
| width: 6px; | |||
| display: inline-block; | |||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| } | |||
| #loadingPage .rect2 { | |||
| -webkit-animation-delay: -1.1s; | |||
| animation-delay: -1.1s; | |||
| } | |||
| #loadingPage .rect3 { | |||
| -webkit-animation-delay: -1.0s; | |||
| animation-delay: -1.0s; | |||
| } | |||
| #loadingPage .rect4 { | |||
| -webkit-animation-delay: -0.9s; | |||
| animation-delay: -0.9s; | |||
| } | |||
| #loadingPage .rect5 { | |||
| -webkit-animation-delay: -0.8s; | |||
| animation-delay: -0.8s; | |||
| } | |||
| @-webkit-keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| -webkit-transform: scaleY(0.4) | |||
| } | |||
| 20% { | |||
| -webkit-transform: scaleY(1.0) | |||
| } | |||
| } | |||
| @keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| transform: scaleY(0.4); | |||
| -webkit-transform: scaleY(0.4); | |||
| } | |||
| 20% { | |||
| transform: scaleY(1.0); | |||
| -webkit-transform: scaleY(1.0); | |||
| } | |||
| } | |||
| .inline.required.field.cloudbrain_benchmark { | |||
| display: none; | |||
| } | |||
| </style> | |||
| <div id="mask"> | |||
| <div id="loadingPage"> | |||
| <div class="rect1"></div> | |||
| <div class="rect2"></div> | |||
| <div class="rect3"></div> | |||
| <div class="rect4"></div> | |||
| <div class="rect5"></div> | |||
| </div> | |||
| </div> | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="repository new repo ui middle very relaxed page grid"> | |||
| <div class="column"> | |||
| {{template "base/alert" .}} | |||
| <div class="ui negative message" id="messageInfo"> | |||
| <p></p> | |||
| </div> | |||
| <form class="ui form" id="form_id" action="{{.Link}}" method="post"> | |||
| {{.CsrfTokenHtml}} | |||
| <h3 class="ui top attached header"> | |||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||
| </h3> | |||
| <div class="ui attached segment"> | |||
| <!-- <br> --> | |||
| <div class="inline required field"> | |||
| <label>任务名称</label> | |||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label>数据集</label> | |||
| <input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | |||
| <datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | |||
| {{range .attachments}} | |||
| <option name="attachment" data-value="{{.UUID}}">{{.Attachment.Name}}</option> | |||
| {{end}} | |||
| </datalist> | |||
| <input type="hidden" name="attachment" id="answerInput-hidden"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>工作环境</label> | |||
| <input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>类型</label> | |||
| <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>规格</label> | |||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||
| {{range .flavors}} | |||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>数据集存放路径</label> | |||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label>描述</label> | |||
| <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label></label> | |||
| <button class="ui green button"> | |||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||
| </button> | |||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| // 取消创建跳转 | |||
| let url_href = window.location.pathname.split('create')[0] | |||
| $(".ui.button").attr('href',url_href) | |||
| // 判断必填选项是否填写正确 | |||
| let form = document.getElementById('form_id'); | |||
| $('#messageInfo').css('display','none') | |||
| form.onsubmit = function(e){ | |||
| let value_task = $("input[name='job_name']").val() | |||
| let re = /^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$/ | |||
| let flag = re.test(value_task) | |||
| if(!flag){ | |||
| $('#messageInfo').css('display','block') | |||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。' | |||
| $('#messageInfo p').text(str) | |||
| return false | |||
| } | |||
| let min_value_task = value_task.toLowerCase() | |||
| $("input[name='job_name']").attr("value",min_value_task) | |||
| document.getElementById("mask").style.display = "block" | |||
| } | |||
| // 点击按钮后遮罩层显示 | |||
| // function showmask() { | |||
| // document.getElementById("mask").style.display = "block" | |||
| // } | |||
| // 页面加载完毕后遮罩层隐藏 | |||
| document.onreadystatechange = function() { | |||
| if (document.readyState === "complete") { | |||
| document.getElementById("mask").style.display = "none" | |||
| } | |||
| } | |||
| $('select.dropdown') | |||
| .dropdown(); | |||
| $(function() { | |||
| $("#cloudbrain_job_type").change(function() { | |||
| if ($(this).val() == 'BENCHMARK') { | |||
| $(".cloudbrain_benchmark").show(); | |||
| } else { | |||
| $(".cloudbrain_benchmark").hide(); | |||
| } | |||
| }) | |||
| }) | |||
| document.querySelector('input[list]').addEventListener('input',function(e){ | |||
| var input = e.target, | |||
| list = input.getAttribute('list'), | |||
| options = document.querySelectorAll('#'+list+' option'), | |||
| hiddenInput = document.getElementById(input.getAttribute('id')+'-hidden'), | |||
| inputValue = input.value; | |||
| hiddenInput.value = inputValue; | |||
| for (let i=0;i<options.length;i++){ | |||
| var option = options[i] | |||
| if(option.innerText===inputValue){ | |||
| hiddenInput.value = option.getAttribute('data-value'); | |||
| break | |||
| } | |||
| } | |||
| }) | |||
| </script> | |||
| @@ -280,9 +280,9 @@ | |||
| <!-- 任务名 --> | |||
| <div class="six wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
| <span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
| </a> | |||
| </div> | |||
| @@ -352,11 +352,11 @@ | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a id="model-delete-{{.JobID}}" class="ui compact {{if eq .Status "RUNNING" "CREATING" "WAITING" "STARTING" "STOPPING" }}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a id="model-delete-{{.JobID}}" class="ui basic button {{if eq .Status "RUNNING" "CREATING" "WAITING" "STARTING" "STOPPING" }}disabled {{else}} blue {{end}}" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| {{$.i18n.Tr "repo.delete"}} | |||
| </a> | |||
| {{else}} | |||
| <a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a class="ui basic blue button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| {{$.i18n.Tr "repo.delete"}} | |||
| </a> | |||
| {{end}} | |||
| @@ -112,7 +112,7 @@ | |||
| <!-- <br> --> | |||
| <div class="inline required field"> | |||
| <label>任务名称</label> | |||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="254"> | |||
| </div> | |||
| <div class="inline field"> | |||
| @@ -128,28 +128,28 @@ | |||
| <div class="inline required field"> | |||
| <label>工作环境</label> | |||
| <input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>类型</label> | |||
| <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>规格</label> | |||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||
| {{range .flavors}} | |||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||
| <option name="flavor" value="{{.Value}}">{{.Desc}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>数据集存放路径</label> | |||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="254" readonly="readonly"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label>描述</label> | |||
| <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | |||
| <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="254"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <label></label> | |||
| @@ -6,7 +6,19 @@ | |||
| {{template "base/alert" .}} | |||
| <h4 class="ui header" id="vertical-segment"> | |||
| <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/cloudbrain"> | |||
| {{.i18n.Tr "repo.cloudbrain"}} | |||
| </a> | |||
| <div class="divider"> / </div> | |||
| <a class="section" href="{{.RepoLink}}/modelarts/notebook"> | |||
| {{$.i18n.Tr "repo.modelarts.notebook"}} | |||
| </a> | |||
| <div class="divider"> / </div> | |||
| {{with .task}} | |||
| <div class="active section">{{.JobName}}</div> | |||
| {{end}} | |||
| </div> | |||
| </h4> | |||
| <div> | |||
| <div class="ui yellow segment"> | |||
| @@ -1,122 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="repository new repo ui middle very relaxed page grid"> | |||
| <div class="column"> | |||
| {{template "base/alert" .}} | |||
| <h4 class="ui header" id="vertical-segment"> | |||
| <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> | |||
| </h4> | |||
| <div> | |||
| <div class="ui yellow segment"> | |||
| {{with .task}} | |||
| <p>任务名称: {{.JobName}}</p> | |||
| {{end}} | |||
| </div> | |||
| <div class="ui green segment"> | |||
| <p>任务结果:</p> | |||
| {{with .result}} | |||
| <table class="ui celled striped table"> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> 状态 </td> | |||
| <td> {{.Status}} </td> | |||
| </tr> | |||
| <tr> | |||
| <td> 开始时间 </td> | |||
| <td>{{.CreateTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 最后更新时间 </td> | |||
| <td>{{.LatestUpdateTime}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| {{end}} | |||
| </div> | |||
| <div class="ui blue segment"> | |||
| {{with .result}} | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> 配置信息 </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> 开发环境类型 </td> | |||
| <td>{{.Profile.DeType}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 硬件类型 </td> | |||
| <td>{{.Profile.FlavorType}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> 机器规格详情 </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> 机器规格 </td> | |||
| <td> {{.Flavor}} </td> | |||
| </tr> | |||
| <tr> | |||
| <td> 规格名称 </td> | |||
| <td>{{.FlavorDetails.Name}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 规格销售状态 </td> | |||
| <td>{{.FlavorDetails.Status}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 排队个数 </td> | |||
| <td>{{.FlavorDetails.QueuingNum}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 排到队的剩余时间(秒) </td> | |||
| <td>{{.FlavorDetails.QueueLeftTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 自动停止时间(秒) </td> | |||
| <td>{{.FlavorDetails.Duration}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| <table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}> | |||
| <thead> | |||
| <tr> <th colspan="2"> 排队信息 </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td> 实例状态 </td> | |||
| <td>{{.QueuingInfo.Status}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 实例排队的开始时间 </td> | |||
| <td>{{.QueuingInfo.BeginTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 排到队的剩余时间(秒) </td> | |||
| <td>{{.QueuingInfo.RemainTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 实例排队的预计停止时间 </td> | |||
| <td>{{.QueuingInfo.EndTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td> 实例在队列中的排位 </td> | |||
| <td>{{.QueuingInfo.Rank}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -18,11 +18,11 @@ | |||
| <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}</h4> | |||
| <div class="required field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
| <input name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" readonly=""> | |||
| <input name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="254" readonly=""> | |||
| </div> | |||
| <div class="field"> | |||
| <label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}</label> | |||
| <textarea id="description" name="description" rows="2"></textarea> | |||
| <textarea id="description" maxlength="254" name="description" rows="2"></textarea> | |||
| </div> | |||
| <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}</h4> | |||
| <div class="required field"> | |||
| @@ -52,7 +52,7 @@ | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
| <input name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255"> | |||
| <input name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="254"> | |||
| <span> | |||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||
| </span> | |||
| @@ -128,7 +128,7 @@ | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||
| <input name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255"> | |||
| <input name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="254"> | |||
| </div> | |||
| <div class="inline field"> | |||
| <button class="ui green button"> | |||
| @@ -180,6 +180,12 @@ | |||
| cursor: pointer; | |||
| pointer-events: none; | |||
| } | |||
| .fontsize14{ | |||
| font-size: 14px; | |||
| } | |||
| .padding0{ | |||
| padding: 0 !important; | |||
| } | |||
| </style> | |||
| <!-- 弹窗 --> | |||
| @@ -232,13 +238,13 @@ | |||
| <div class="column"> | |||
| <div class="ui blue small menu compact selectcloudbrain"> | |||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||
| <a class="item" href="{{.RepoLink}}/cloudbrain">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||
| <a class="active item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> | |||
| </div> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
| <!-- <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
| {{svg "octicon-server" 16}} | |||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||
| <i class="dropdown icon"></i> | |||
| @@ -246,7 +252,7 @@ | |||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="22">Ascend NPU</a> | |||
| </div> | |||
| </div> | |||
| </div> --> | |||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/train-job/create">{{$.i18n.Tr "repo.modelarts.train_job.new_train"}}</a>{{end}} | |||
| </div> | |||
| @@ -278,20 +284,29 @@ | |||
| <!-- 表头 --> | |||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||
| <div class="row"> | |||
| <div class="five wide column"> | |||
| <div class="three wide column padding0"> | |||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||
| </div> | |||
| <div class="three wide column"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span> | |||
| <div class="one wide column text center padding0"> | |||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.modelarts.version_nums"}}</span> | |||
| </div> | |||
| <div class="two wide column"> | |||
| <div class="two wide column text center padding0"> | |||
| <span>{{$.i18n.Tr "repo.modelarts.status"}}</span> | |||
| </div> | |||
| <div class="two wide column text center padding0"> | |||
| <span>{{$.i18n.Tr "repo.modelarts.createtime"}}</span> | |||
| </div> | |||
| <div class="two wide column text center padding0"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span> | |||
| </div> | |||
| <div class="one wide column text center"> | |||
| <div class="two wide column text center padding0"> | |||
| <span>{{$.i18n.Tr "repo.modelarts.computing_resources"}}</span> | |||
| </div> | |||
| <div class="one wide column text center padding0"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||
| </div> | |||
| <div class="five wide column text center"> | |||
| <span style="margin-left: 6rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||
| <div class="three wide column text center padding0"> | |||
| <span>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||
| </div> | |||
| </div> | |||
| @@ -305,38 +320,44 @@ | |||
| <div class="row"> | |||
| <!-- 任务名 --> | |||
| <div class="five wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||
| <div class="three wide column padding0"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
| <span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
| </a> | |||
| </div> | |||
| <div class="three wide column"> | |||
| <!--任务状态 --> | |||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| {{.Status}} | |||
| </span> --> | |||
| <!-- 版本数量 --> | |||
| <div class="one wide column text center padding0"> | |||
| <span style="font-size: 12px;">{{.VersionCount}} </span> | |||
| </div> | |||
| <!-- 任务状态 --> | |||
| <div class="two wide column padding0" style="padding-left: 2.2rem !important;"> | |||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
| <span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| </span> | |||
| </div> | |||
| <!-- 任务创建时间 --> | |||
| <div class="two wide column text center padding0"> | |||
| <span style="font-size: 12px;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||
| </div> | |||
| <!-- <div class="two wide column"> | |||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| <span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| </span> | |||
| <!-- 任务创建时间 --> | |||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||
| </div> --> | |||
| <!-- 任务运行时间 --> | |||
| <div class="two wide column text center padding0"> | |||
| <span style="font-size: 12px;" id="duration-{{.JobID}}"></span> | |||
| </div> | |||
| <div class="two wide column"> | |||
| <!--任务状态 --> | |||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||
| {{.Status}} | |||
| </span> --> | |||
| <span id="duration-{{.JobID}}"></span> | |||
| <!-- 任务创建时间 --> | |||
| <!-- <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> --> | |||
| <!-- 计算资源 --> | |||
| <div class="two wide column text center padding0"> | |||
| <span style="font-size: 12px;">{{.ComputeResource}}</span> | |||
| </div> | |||
| <div class="one wide column text center"> | |||
| <!-- 创建者 --> | |||
| <div class="one wide column text center padding0"> | |||
| {{if .User.Name}} | |||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||
| {{else}} | |||
| @@ -344,56 +365,55 @@ | |||
| {{end}} | |||
| </div> | |||
| <div class="five wide column text right"> | |||
| <div class="ui compact buttons"> | |||
| <!-- <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}"> | |||
| 查看 | |||
| </a> | |||
| <a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||
| 调试 | |||
| </a> --> | |||
| <form id="stopForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/stop" method="post" style="margin-left:-1px;"> | |||
| <div class="three wide column text center padding0"> | |||
| <!-- <div class="ui compact buttons"> | |||
| <form id="stopForm-{{.JobID}}" action="/api/v1/repos{{$.Link}}/{{.JobID}}/stop_version" method="post"> | |||
| <input type="hidden" name="version_name" value="{{.VersionName}}"> | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a id="stop-model-debug-{{.JobID}}" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
| <a style="padding: 0.5rem 1rem;" id="stop-model-debug-{{.JobID}}" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| {{else}} | |||
| <a class="ui basic disabled button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
| <a style="padding: 0.5rem 1rem;" class="ui basic disabled button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| {{end}} | |||
| </form> | |||
| </div> | |||
| <div class="ui compact buttons" style="margin-right:10px;"> | |||
| <!-- 模型下载 --> | |||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | |||
| </div> --> | |||
| <!-- 模型下载 --> | |||
| <!-- <div class="ui compact buttons"> | |||
| <a style="padding: 0.5rem;" class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | |||
| {{$.i18n.Tr "repo.model_download"}} | |||
| </a> | |||
| <!-- 接收结果 --> | |||
| <!-- <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | |||
| <a class="imageBtn ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" value="{{.CanDebug}}">提交镜像</a> --> | |||
| </div> | |||
| </div> --> | |||
| <!-- 删除任务 --> | |||
| <div class="ui compact buttons"> | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a style="padding: 0.5rem 1rem;" id="{{.VersionName}}-stop" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}} blue {{end}}button" onclick="stopVersion({{.VersionName}},{{.JobID}})"> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| {{else}} | |||
| <a style="padding: 0.5rem 1rem;" id="{{.VersionName}}-stop" class="ui basic disabled button"> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| {{end}} | |||
| </div> | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a id="model-delete-{{.JobID}}" class="ui compact {{if or (eq .Status "RUNNING") (eq .Status "INIT") (eq .Status "CREATING") (eq .Status "WAITING") }}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui basic blue button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| {{$.i18n.Tr "repo.delete"}} | |||
| </a> | |||
| {{else}} | |||
| <a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a style="padding: 0.5rem 1rem;margin-left:0.2rem" class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| {{$.i18n.Tr "repo.delete"}} | |||
| </a> | |||
| {{end}} | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{end}} {{template "base/paginate" .}} | |||
| @@ -435,6 +455,8 @@ | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| console.log({{.Tasks}}) | |||
| // 调试和评分新开窗口 | |||
| function stop(obj) { | |||
| if (obj.style.color != "rgb(204, 204, 204)") { | |||
| @@ -484,11 +506,12 @@ | |||
| $(".job-status").each((index, job) => { | |||
| const jobID = job.dataset.jobid; | |||
| const repoPath = job.dataset.repopath; | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => { | |||
| const versionname = job.dataset.version | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => { | |||
| console.log(data) | |||
| const duration = data.JobDuration | |||
| const jobID = data.JobID | |||
| let train_duration = runtime(duration) | |||
| $('#duration-'+jobID).text(train_duration) | |||
| $('#duration-'+jobID).text(duration) | |||
| }) | |||
| }) | |||
| @@ -501,50 +524,24 @@ | |||
| $(".job-status").each((index, job) => { | |||
| const jobID = job.dataset.jobid; | |||
| const repoPath = job.dataset.repopath; | |||
| const versionname = job.dataset.version | |||
| if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED' | |||
| || job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED' | |||
| || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') { | |||
| return | |||
| } | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => { | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => { | |||
| const jobID = data.JobID | |||
| const status = data.JobStatus | |||
| const duration = data.JobDuration | |||
| $('#duration-'+jobID).text(duration) | |||
| if (status != job.textContent.trim()) { | |||
| $('#' + jobID+'-icon').removeClass().addClass(status) | |||
| $('#' + jobID+ '-text').text(status) | |||
| } | |||
| if(status==="RUNNING"){ | |||
| $('#model-debug-'+jobID).removeClass('disabled') | |||
| $('#model-debug-'+jobID).addClass('blue') | |||
| let train_duration = runtime(duration) | |||
| $('#duration-'+jobID).text(train_duration) | |||
| } | |||
| if(status!=="RUNNING"){ | |||
| $('#model-debug-'+jobID).removeClass('blue') | |||
| $('#model-debug-'+jobID).addClass('disabled') | |||
| } | |||
| if(status!=="KILLED" || status!=="FAILED"){ | |||
| $('#stop-model-debug-'+jobID).removeClass('disabled') | |||
| $('#stop-model-debug-'+jobID).addClass('blue') | |||
| $('#model-delete-'+jobID).removeClass('red') | |||
| $('#model-delete-'+jobID).addClass('disabled') | |||
| } | |||
| if(status=="KILLED" || status=="FAILED" || status=="KILLING"){ | |||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||
| $('#model-delete-'+jobID).removeClass('disabled') | |||
| $('#model-delete-'+jobID).addClass('red') | |||
| } | |||
| if(status=="START_FAILED"){ | |||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| @@ -580,7 +577,36 @@ | |||
| modal.style.display = "none"; | |||
| } | |||
| } | |||
| function stopVersion(version_name,jobID){ | |||
| const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/train-job/'+jobID+'/stop_version' | |||
| $.post(url,{version_name:version_name},(data)=>{ | |||
| if(data.StatusOK===0){ | |||
| $('#'+version_name+'-stop').removeClass('blue') | |||
| $('#'+version_name+'-stop').addClass('disabled') | |||
| refreshStatus(version_name,jobID) | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| function refreshStatus(version_name,jobID){ | |||
| const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/train-job/'+jobID+'?version_name='+version_name | |||
| $.get(url,(data)=>{ | |||
| $(`#${jobID}-icon`).attr("class",data.JobStatus) | |||
| // detail status and duration | |||
| $(`#${jobID}-text`).text(data.JobStatus) | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| // 显示弹窗,弹出相应的信息 | |||
| function showmask() { | |||
| $('#imageModal').css('display', 'none') | |||
| @@ -103,7 +103,9 @@ | |||
| -webkit-animation-delay: -0.8s; | |||
| animation-delay: -0.8s; | |||
| } | |||
| .left2{ | |||
| margin-left: -2px; | |||
| } | |||
| @-webkit-keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| @@ -153,95 +155,81 @@ | |||
| <form class="ui form" action="{{.Link}}" method="post"> | |||
| {{.CsrfTokenHtml}} | |||
| <input type="hidden" name="action" value="update"> | |||
| <input type="hidden" id="ai_engine_name" name="engine_names" value=""> | |||
| <input type="hidden" id="ai_flaver_name" name="flaver_names" value=""> | |||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
| <div class="required unite min_title inline field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
| <input style="width: 60%;" name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
| <input style="width: 60%;" name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="254"> | |||
| </div> | |||
| <div class="unite min_title inline field"> | |||
| <label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}} </label> | |||
| <textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)"></textarea> | |||
| <label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}} </label> | |||
| <textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="254" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea> | |||
| </div> | |||
| <!-- <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}</h4> | |||
| <div class="inline field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.fast_parameter_setting"}}</label> | |||
| <span> | |||
| {{.i18n.Tr "repo.modelarts.train_job.fast_parameter_setting_config"}} | |||
| <a class="item active parameter_config">{{.i18n.Tr "repo.modelarts.train_job.fast_parameter_setting_config_link"}}</a> | |||
| </span> | |||
| </div> --> | |||
| <div class="ui divider"></div> | |||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4> | |||
| <div class="required unite min_title inline field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label> | |||
| <select class="ui dropdown width80 left2" id="code_version" name="branch_name"> | |||
| {{if .branch_name}} | |||
| <option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option> | |||
| {{range $k, $v :=.Branches}} | |||
| {{ if ne $v $.branch_name }} | |||
| <option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| {{else}} | |||
| <option name="branch_name" value="{{.BranchName}}">{{.BranchName}}</option> | |||
| {{range $k, $v :=.Branches}} | |||
| {{ if ne $v $.BranchName }} | |||
| <option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="required unite min_title inline fields" style="width: 90%;"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}} </label> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}} </label> | |||
| <div class="field" style="flex: 1.5;"> | |||
| <select class="ui dropdown width" id="trainjob_engines" > | |||
| {{range .engines}} | |||
| <option value="{{.Value}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="field" style="flex: 2;"> | |||
| <div class="field" style="flex: 2;" id="engine_name"> | |||
| <select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;' name="engine_id"> | |||
| {{range .engine_versions}} | |||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| </div> | |||
| <!-- <div class="required inline field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.algorithm_origin"}}</label> | |||
| <div class="ui top attached tabular menu"> | |||
| <a class="item active" data-tab="frame">{{svg "octicon-repo" 16}}{{.i18n.Tr "repo.modelarts.train_job.frames"}}</a> | |||
| </div> | |||
| <div class="ui bottom attached tab active segment" data-tab="frame"> | |||
| <div class="required field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</label> | |||
| <div class="two fields"> | |||
| <div class="field"> | |||
| <select class="ui search dropdown" id="trainjob_engines" style='width:385px'> | |||
| {{range .engines}} | |||
| <option value="{{.Value}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="field"> | |||
| <select class="ui search dropdown" id="trainjob_engine_versions" style='width:385px' name="engine_id"> | |||
| {{range .engine_versions}} | |||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="inline required field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
| <input name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255"> | |||
| <span> | |||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||
| </span> | |||
| </div> | |||
| </div> | |||
| </div> --> | |||
| <div class="inline unite min_title field required"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
| {{if .bootFile}} | |||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" > | |||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="254" > | |||
| {{else}} | |||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" > | |||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="254" > | |||
| {{end}} | |||
| <span> | |||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||
| </span> | |||
| </div> | |||
| <div class="required unite min_title inline field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label> | |||
| <select class="ui dropdown width80" id="trainjob_datasets" name="attachment" placeholder="选择数据集"> | |||
| {{if $.uuid}} | |||
| <option name="attachment" value="{{$.uuid}}">{{$.datasetName}}</option> | |||
| @@ -254,19 +242,32 @@ | |||
| </div> | |||
| <div class="inline unite min_title field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||
| <!-- <i class="plus square outline icon"></i> --> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||
| <span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | |||
| <input id="store_run_para" type="hidden" name="run_para_list"> | |||
| <div class="dynamic field" style="margin-top: 1rem;"></div> | |||
| <!-- <div class="dynamic field"> | |||
| </div> --> | |||
| <div class="dynamic field" style="margin-top: 1rem;"> | |||
| {{if ne 0 (len .params)}} | |||
| {{range $k ,$v := .params}} | |||
| <div class="two fields width85" id="para{{$k}}"> | |||
| <div class="field"> | |||
| <input type="text" name="shipping_first-name" value={{$v.Label}} required> | |||
| </div> | |||
| <div class="field"> | |||
| <input type="text" name="shipping_last-name" value={{$v.Value}} required> | |||
| </div> | |||
| <span> | |||
| <i class="trash icon"></i> | |||
| </span> | |||
| </div> | |||
| {{end}} | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| <!-- <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.resource_setting"}}</h4> --> | |||
| <div class="required field " style="display: none;"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label> | |||
| <select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id"> | |||
| {{range .resource_pools}} | |||
| <option value="{{.ID}}">{{.Value}}</option> | |||
| @@ -275,7 +276,7 @@ | |||
| </div> | |||
| <div class="required grouped fields" style="display: none;"> | |||
| <label for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label> | |||
| <label style="font-weight: normal;" for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label> | |||
| <div class="field"> | |||
| <div class="ui grid"> | |||
| <div class="column"> | |||
| @@ -290,8 +291,8 @@ | |||
| </div> | |||
| </div> | |||
| <div class="required unite min_title inline field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||
| <div class="required unite min_title inline field" id="flaver_name"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||
| <select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor"> | |||
| {{range .flavor_infos}} | |||
| <option name="flavor" value="{{.Code}}">{{.Value}}</option> | |||
| @@ -299,39 +300,15 @@ | |||
| </select> | |||
| </div> | |||
| <div class="inline required unite min_title field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||
| <div class="ui labeled input" style="width: 5%;"> | |||
| <!-- <span class="min"><i class="minus icon"></i></span> --> | |||
| <input style="border-radius: 0;text-align: center;" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" readonly> | |||
| <input style="border-radius: 0;text-align: center;" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="254" value="1" readonly> | |||
| <!-- <span class="add"><i class="plus icon"></i></span> --> | |||
| </div> | |||
| <!-- <input name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255"> --> | |||
| </div> | |||
| <!-- | |||
| <div class="inline field"> | |||
| <div class="ui save checkbox"> | |||
| <input name="is_save_para" type="checkbox"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.query_whether_save_parameter"}} | |||
| <span> | |||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.save_helper"}} data-position="right center" data-variation="mini"></i> | |||
| </span> | |||
| </label> | |||
| </div> | |||
| </div> | |||
| <div class="disabled field" id="save_para"> | |||
| <div class="field"> | |||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_parameter_name"}}</label> | |||
| <input name="parameter_template_name" id="parameter_template_name" tabindex="3" autofocus maxlength="255"> | |||
| </div> | |||
| <div class="field"> | |||
| <label for="parameter_description">{{.i18n.Tr "repo.modelarts.train_job.parameter_description"}}</label> | |||
| <textarea id="parameter_description" name="parameter_description" rows="2"></textarea> | |||
| </div> | |||
| </div> | |||
| --> | |||
| <div class="inline unite min_title field"> | |||
| <button class="ui create_train_job green button"> | |||
| @@ -598,8 +575,15 @@ | |||
| msg = JSON.stringify(msg) | |||
| $('#store_run_para').val(msg) | |||
| } | |||
| function get_name(){ | |||
| let name1=$("#engine_name .text").text() | |||
| let name2=$("#flaver_name .text").text() | |||
| $("input#ai_engine_name").val(name1) | |||
| $("input#ai_flaver_name").val(name2) | |||
| } | |||
| $('.ui.create_train_job.green.button').click(function(e) { | |||
| get_name() | |||
| send_run_para() | |||
| validate() | |||
| }) | |||
| @@ -38,7 +38,7 @@ | |||
| <!-- 任务名 --> | |||
| <div class="five wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}"> | |||
| <span class="fitted">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted">{{.JobName}}</span> | |||
| </a> | |||
| </div> | |||
| @@ -1,139 +1,470 @@ | |||
| {{template "base/head" .}} | |||
| <style> | |||
| .according-panel-heading{ | |||
| box-sizing: border-box; | |||
| padding: 8px 16px; | |||
| color: #252b3a; | |||
| background-color: #f2f5fc; | |||
| line-height: 1.5; | |||
| cursor: pointer; | |||
| -moz-user-select: none; | |||
| -webkit-user-select: none; | |||
| -ms-user-select: none; | |||
| -khtml-user-select: none; | |||
| user-select: none; | |||
| } | |||
| .accordion-panel-title { | |||
| margin-top: 0; | |||
| margin-bottom: 0; | |||
| color: #252b3a; | |||
| } | |||
| .accordion-panel-title-content{ | |||
| vertical-align: middle; | |||
| display: inline-block; | |||
| width: calc(100% - 32px); | |||
| cursor: default; | |||
| } | |||
| .acc-margin-bottom { | |||
| margin-bottom: 5px; | |||
| } | |||
| .title_text { | |||
| font-size: 12px; | |||
| } | |||
| .ac-display-inblock { | |||
| display: inline-block; | |||
| } | |||
| .cti-mgRight-sm { | |||
| margin-right: 8px; | |||
| } | |||
| .ac-text-normal { | |||
| font-size: 14px; | |||
| color: #575d6c; | |||
| } | |||
| .uc-accordionTitle-black { | |||
| color: #333; | |||
| } | |||
| .accordion-border{ | |||
| border:1px solid #cce2ff; | |||
| } | |||
| .padding0{ | |||
| padding: 0 !important; | |||
| } | |||
| .content-pad{ | |||
| padding: 15px 35px; | |||
| } | |||
| .content-margin{ | |||
| margin:10px 5px ; | |||
| } | |||
| .tab_2_content { | |||
| min-height: 360px; | |||
| margin-left: 10px; | |||
| } | |||
| .ac-grid { | |||
| display: block; | |||
| *zoom: 1; | |||
| } | |||
| .ac-grid-col { | |||
| float: left; | |||
| width: 100%; | |||
| } | |||
| .ac-grid-col2 .ac-grid-col { | |||
| width: 50%; | |||
| } | |||
| .ti-form { | |||
| text-align: left; | |||
| max-width: 100%; | |||
| vertical-align: middle; | |||
| } | |||
| .ti-form>tbody { | |||
| font-size: 12px; | |||
| } | |||
| .ti-form>tbody, .ti-form>tbody>tr { | |||
| vertical-align: inherit; | |||
| } | |||
| .ti-text-form-label { | |||
| padding-bottom: 20px; | |||
| padding-right: 20px; | |||
| color: #8a8e99; | |||
| font-size: 12px; | |||
| white-space: nowrap !important; | |||
| width: 80px; | |||
| line-height: 30px; | |||
| } | |||
| .ti-text-form-content{ | |||
| line-height: 30px; | |||
| padding-bottom: 20px; | |||
| } | |||
| .ti-form>tbody>tr>td { | |||
| vertical-align: top; | |||
| white-space: normal; | |||
| } | |||
| td, th { | |||
| padding: 0; | |||
| } | |||
| .ac-grid-col .text-span { | |||
| width: 450px; | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| white-space: nowrap; | |||
| } | |||
| .redo-color{ | |||
| color: #3291F8; | |||
| } | |||
| .ti-action-menu-item:not(:last-child){ | |||
| margin-right: 10px; | |||
| padding-right: 11px; | |||
| text-decoration: none!important; | |||
| color: #526ecc; | |||
| cursor: pointer; | |||
| display: inline-block; | |||
| -moz-user-select: none; | |||
| -webkit-user-select: none; | |||
| -ms-user-select: none; | |||
| -khtml-user-select: none; | |||
| user-select: none; | |||
| position: relative; | |||
| } | |||
| .ti-action-menu-item:not(:last-child):after { | |||
| content: ""; | |||
| display: inline-block; | |||
| position: absolute; | |||
| height: 12px; | |||
| right: 0; | |||
| top: 50%; | |||
| -webkit-transform: translateY(-6px); | |||
| -ms-transform: translateY(-6px); | |||
| -o-transform: translateY(-6px); | |||
| transform: translateY(-6px); | |||
| border-right: 1px solid #dfe1e6; | |||
| } | |||
| .text-width80{ | |||
| width: 100px; | |||
| line-height: 30px; | |||
| } | |||
| .border-according{ | |||
| border: 1px solid #dfe1e6; | |||
| } | |||
| .disabled { | |||
| cursor: default; | |||
| pointer-events: none; | |||
| color: rgba(0,0,0,.6) !important; | |||
| opacity: .45 !important; | |||
| } | |||
| .pad20{ | |||
| border:0px !important; | |||
| } | |||
| .model_file_bread{ | |||
| margin-bottom: -0.5rem !important; | |||
| padding-left: 1rem; | |||
| padding-top: 0.5rem ; | |||
| } | |||
| </style> | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="repository new repo ui middle very relaxed page grid"> | |||
| <div class="column"> | |||
| {{template "base/alert" .}} | |||
| <h4 class="ui top attached header"> | |||
| <div class="ui two column grid"> | |||
| <div class="column"> | |||
| {{$.i18n.Tr "repo.modelarts.version_manage"}} | |||
| <div class="ui container"> | |||
| <h4 class="ui header" id="vertical-segment"> | |||
| <!-- <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> --> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/cloudbrain"> | |||
| {{.i18n.Tr "repo.cloudbrain"}} | |||
| </a> | |||
| <div class="divider"> / </div> | |||
| <a class="section" href="{{$.RepoLink}}/modelarts/train-job"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job"}} | |||
| </a> | |||
| <div class="divider"> / </div> | |||
| <div class="active section">{{.jobName}}</div> | |||
| </div> | |||
| </h4> | |||
| {{range $k ,$v := .version_list_task}} | |||
| <div class="ui accordion border-according" id="accordion{{.VersionName}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
| <div class="{{if eq $k 0}}active{{end}} title padding0"> | |||
| <div class="according-panel-heading"> | |||
| <div class="accordion-panel-title"> | |||
| <i class="dropdown icon"></i> | |||
| <span class="accordion-panel-title-content"> | |||
| <span> | |||
| <div style="float: right;"> | |||
| <!-- <a class="ti-action-menu-item {{if ne .Status "COMPLETED"}}disabled {{end}}">创建模型</a> --> | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.canNewJob}} | |||
| <a class="ti-action-menu-item" href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">{{$.i18n.Tr "repo.modelarts.modify"}}</a> | |||
| {{else}} | |||
| <a class="ti-action-menu-item disabled" href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">{{$.i18n.Tr "repo.modelarts.modify"}}</a> | |||
| {{end}} | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a class="ti-action-menu-item {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{end}}" id="{{.VersionName}}-stop" onclick="stopVersion({{.VersionName}})">{{$.i18n.Tr "repo.stop"}}</a> | |||
| {{else}} | |||
| <a class="ti-action-menu-item disabled" id="{{.VersionName}}-stop" onclick="stopVersion({{.VersionName}})">{{$.i18n.Tr "repo.stop"}}</a> | |||
| {{end}} | |||
| {{$.CsrfTokenHtml}} | |||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
| <a class="ti-action-menu-item" onclick="deleteVersion({{.VersionName}})" style="color: #FF4D4F;">{{$.i18n.Tr "repo.delete"}}</a> | |||
| {{else}} | |||
| <a class="ti-action-menu-item disabled" onclick="deleteVersion({{.VersionName}})" style="color: #FF4D4F;">{{$.i18n.Tr "repo.delete"}}</a> | |||
| {{end}} | |||
| </div> | |||
| <div class="ac-display-inblock title_text acc-margin-bottom"> | |||
| <span class="cti-mgRight-sm">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span> | |||
| <span class="cti-mgRight-sm"> {{$.i18n.Tr "repo.modelarts.current_version"}}:{{.VersionName}}</span> | |||
| <span class="cti-mgRight-sm"> {{$.i18n.Tr "repo.modelarts.parent_version"}}:{{.PreVersionName}}</span> | |||
| <span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}: | |||
| <span id="{{.VersionName}}-status-span"><i id="icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| </span> | |||
| <span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}:</span> | |||
| <span class="cti-mgRight-sm uc-accordionTitle-black" id="{{.VersionName}}-duration-span">{{.TrainJobDuration}}</span> | |||
| <span data-tooltip="刷新" style="cursor: pointer;" data-inverted="" onclick="refreshStatus({{.VersionName}})"><i class="redo icon redo-color"></i></span> | |||
| </div> | |||
| </span> | |||
| </span> | |||
| </div> | |||
| <div class="column right aligned"> | |||
| <a href="javascript:window.history.back();">{{svg "octicon-reply" 16}}{{$.i18n.Tr "repo.modelarts.back"}}</a> | |||
| </div> | |||
| </div> | |||
| <div class="{{if eq $k 0}}active{{end}} content"> | |||
| <div class="content-pad"> | |||
| <div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);"> | |||
| <a class="active item" data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||
| <a class="item" data-tab="second{{$k}}" onclick="loadLog({{.VersionName}})">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||
| <a class="item" data-tab="third{{$k}}" onclick="loadModelFile({{.VersionName}},'','','init')">{{$.i18n.Tr "repo.model_download"}}</a> | |||
| </div> | |||
| </div> | |||
| </h4> | |||
| <div class="ui tab active" data-tab="first{{$k}}"> | |||
| <div style="padding-top: 10px;"> | |||
| <div class="tab_2_content"> | |||
| <div class="ac-grid ac-grid-col2"> | |||
| <div class="ac-grid-col"> | |||
| <table class="ti-form"> | |||
| <tbody class="ti-text-form"> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.cloudbrain_task"}} | |||
| </td> | |||
| <div class="ui attached segment"> | |||
| <div class="ui style accordion"> | |||
| <div class="title active"> | |||
| <i class="dropdown icon"></i> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.version"}} | |||
| </div> | |||
| <div class="content active"> | |||
| <div class="ui container"> | |||
| <div class="ui top attached tabular menu"> | |||
| <a class="item active" data-tab="configs">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||
| <a class="item logs" data-tab="logs">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||
| <!-- <a class="item" data-tab="resources">资源占用情况</a> --> | |||
| </div> | |||
| <div class="ui bottom attached tab segment active" data-tab="configs"> | |||
| <div> | |||
| <div class="ui yellow segment"> | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> {{.i18n.Tr "repo.modelarts.train_job.basic_info"}} </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.job_name"}} </td> | |||
| <td>{{.result.JobName}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.job_status"}} </td> | |||
| <td>{{.result.Status}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.version"}} </td> | |||
| <td>{{.result.VersionName}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.start_time"}} </td> | |||
| <td>{{.result.CreateTime}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.dura_time"}} </td> | |||
| <td>{{.result.TrainJobDuration}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.description"}} </td> | |||
| <td>{{.result.Description}}</td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| <div class="ui green segment"> | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> {{.i18n.Tr "repo.modelarts.train_job.parameter_setting_info"}} </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.AI_driver"}} </td> | |||
| <td>{{.result.EngineName}} | {{.result.EngineVersion}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.start_file"}}</td> | |||
| <td>{{.result.BootFileUrl}}</td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.JobName}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.status"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w" id="{{.VersionName}}-status"> | |||
| {{.Status}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.run_version"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.VersionName}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.start_time"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| <span style="font-size: 12px;" class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.dura_time"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w" id="{{.VersionName}}-duration"> | |||
| {{.TrainJobDuration}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.standard"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.FlavorName}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.compute_node"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.WorkServerNumber}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| <div class="ac-grid-col"> | |||
| <table class="ti-form"> | |||
| <tbody class="ti-text-form"> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.AI_driver"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.EngineName}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.dataset"}} </td> | |||
| <td>{{.result.DatasetName}}</td> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.code_version"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BranchName}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.run_parameter"}} </td> | |||
| <td>{{.result.Parameter}}</td> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.start_file"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BootFile}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| <div class="ui blue segment"> | |||
| <table class="ui celled striped table"> | |||
| <thead> | |||
| <tr> <th colspan="2"> {{.i18n.Tr "repo.modelarts.train_job.resource_setting_info"}} </th> </tr> | |||
| </thead> | |||
| <tbody> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.resource_pool"}} </td> | |||
| <td>{{.result.PoolName}}</td> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.DatasetName}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</td> | |||
| <td>{{.result.WorkServerNum}}</td> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80" > | |||
| {{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w" title="{{.Parameters}}"> | |||
| {{.Parameters}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.NAS_mount_path"}} </td> | |||
| <td>{{.result.NasMountPath}}</td> | |||
| <!-- <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| 训练输出位置 | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.TrainUrl}} | |||
| </div> | |||
| </td> | |||
| </tr> --> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| {{$.i18n.Tr "repo.modelarts.train_job.description"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w" title="{{.Cloudbrain.Description}}"> | |||
| {{.Cloudbrain.Description}} | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="ui bottom attached tab segment" data-tab="logs"> | |||
| <div class="ui message" style="display: none;"> | |||
| <div class="header"> | |||
| </div> | |||
| </div> | |||
| <div class="ui top attached segment" style="background: #f0f0f0;"> | |||
| <div class="center aligned"> | |||
| <label>{{$.i18n.Tr "repo.modelarts.log"}}:</label> | |||
| <span class="fitted file_name">{{.log_file_name}}</span> | |||
| <input type="hidden" name="file_name" value={{.log_file_name}}> | |||
| <input type="hidden" name="start_line" value={{.log.StartLine}}> | |||
| <input type="hidden" name="end_line" value={{.log.EndLine}}> | |||
| </div> | |||
| </div> | |||
| <div class="ui attached segment log" style="height: 300px !important; overflow: auto;"> | |||
| <pre>{{.log.Content}}</pre> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="ui tab" data-tab="second{{$k}}"> | |||
| <div> | |||
| <div class="ui message message{{.VersionName}}" style="display: none;"> | |||
| <div id="header"></div> | |||
| </div> | |||
| <!-- <div class="ui top attached segment" style="background: #f0f0f0;"> | |||
| <div class="center aligned"> | |||
| <label>{{$.i18n.Tr "repo.modelarts.log"}}:</label> | |||
| </div> | |||
| </div> | |||
| </div> --> | |||
| <div class="ui attached log" onscroll="logScroll({{.VersionName}})" id="log{{.VersionName}}" style="height: 300px !important; overflow: auto;"> | |||
| <!-- <input type="hidden" class="version_name" name="version_name" value={{.VersionName}}> --> | |||
| <input type="hidden" name="end_line" value> | |||
| <input type="hidden" name="start_line" value> | |||
| <pre id="log_file{{.VersionName}}"></pre> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="ui tab" data-tab="third{{$k}}"> | |||
| <input type="hidden" name="model{{.VersionName}}" value="-1"> | |||
| <input type="hidden" name="modelback{{.VersionName}}" value="-1"> | |||
| <div class='ui breadcrumb model_file_bread' id='file_breadcrumb{{.VersionName}}'> | |||
| <div class="active section">{{.VersionName}}</div> | |||
| <div class="divider"> / </div> | |||
| </div> | |||
| <div id="dir_list{{.VersionName}}"> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{end}} {{template "base/paginate" .}} | |||
| </div> | |||
| <!-- 确认模态框 --> | |||
| <div id="deletemodel"> | |||
| <div class="ui basic modal"> | |||
| <div class="ui icon header"> | |||
| <i class="trash icon"></i> 删除任务 | |||
| </div> | |||
| <div class="content"> | |||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||
| </div> | |||
| <div class="actions"> | |||
| <div class="ui red basic inverted cancel button"> | |||
| <i class="remove icon"></i> 取消操作 | |||
| </div> | |||
| <div class="ui green basic inverted ok button"> | |||
| <i class="checkmark icon"></i> 确定操作 | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -142,59 +473,285 @@ | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| console.log({{.version_list_task}}) | |||
| $('.menu .item').tab() | |||
| $('.ui.style.accordion').accordion(); | |||
| // $('.ui.style.accordion').accordion(); | |||
| var userName | |||
| var repoPath | |||
| var jobID | |||
| $(document).ready(function(){ | |||
| var url = window.location.href; | |||
| var urlArr = url.split('/') | |||
| $('.ui.accordion').accordion({selector:{trigger:'.icon'}}); | |||
| }); | |||
| $(document).ready(function(){ | |||
| $('.secondary.menu .item').tab(); | |||
| }); | |||
| let userName | |||
| let repoPath | |||
| let jobID | |||
| $(document).ready(function(){ | |||
| let url = window.location.href; | |||
| let urlArr = url.split('/') | |||
| userName = urlArr.slice(-5)[0] | |||
| repoPath = urlArr.slice(-4)[0] | |||
| jobID = urlArr.slice(-1)[0] | |||
| }) | |||
| function stopBubbling(e) { | |||
| e = window.event || e; | |||
| if (e.stopPropagation) { | |||
| e.stopPropagation(); //阻止事件 冒泡传播 | |||
| } else { | |||
| e.cancelBubble = true; //ie兼容 | |||
| } | |||
| } | |||
| // let timeid = window.setInterval(refreshStatus(version_name), 30000); | |||
| // document.ready(refreshStatus(version_name)) | |||
| let timeid = window.setInterval(loadJobStatus, 30000); | |||
| $(document).ready(loadJobStatus); | |||
| $(".log").scroll(function () { | |||
| var scrollTop = $(this)[0].scrollTop; // 滚动距离 | |||
| var scrollHeight = $(this)[0].scrollHeight; // 文档高度 | |||
| var divHeight = $(this).height(); // 可视区高度 | |||
| var file_name = $('input[name=file_name]').val() | |||
| function renderSize(value){ | |||
| if(null==value||value==''){ | |||
| return "0 Bytes"; | |||
| } | |||
| var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"); | |||
| var index=0; | |||
| var srcsize = parseFloat(value); | |||
| index=Math.floor(Math.log(srcsize)/Math.log(1024)); | |||
| var size =srcsize/Math.pow(1024,index); | |||
| size=size.toFixed(2);//保留的小数位数 | |||
| return size+unitArr[index]; | |||
| } | |||
| function loadJobStatus() { | |||
| $(".ui.accordion.border-according").each((index, job) => { | |||
| const jobID = job.dataset.jobid; | |||
| const repoPath = job.dataset.repopath; | |||
| const versionname = job.dataset.version | |||
| if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED' | |||
| || job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED' | |||
| || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') { | |||
| return | |||
| } | |||
| let stopArray=["KILLED","FAILED","START_FAILED","KILLING","COMPLETED"] | |||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => { | |||
| $(`#${versionname}-duration-span`).text(data.JobDuration) | |||
| $(`#${versionname}-status-span span`).text(data.JobStatus) | |||
| $(`#${versionname}-status-span i`).attr("class",data.JobStatus) | |||
| // detail status and duration | |||
| $('#'+versionname+'-duration').text(data.JobDuration) | |||
| $('#'+versionname+'-status').text(data.JobStatus) | |||
| if(stopArray.includes(data.JobStatus)){ | |||
| $('#'+versionname+'-stop').addClass('disabled') | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| }); | |||
| }; | |||
| function refreshStatus(version_name){ | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}?version_name=${version_name}`,(data)=>{ | |||
| // header status and duration | |||
| $(`#${version_name}-duration-span`).text(data.JobDuration) | |||
| $(`#${version_name}-status-span span`).text(data.JobStatus) | |||
| $(`#${version_name}-status-span i`).attr("class",data.JobStatus) | |||
| // detail status and duration | |||
| $('#'+version_name+'-duration').text(data.JobDuration) | |||
| $('#'+version_name+'-status').text(data.JobStatus) | |||
| loadLog(version_name) | |||
| if(parseInt(scrollTop) + divHeight + 29 == scrollHeight){ | |||
| var end_line = $('input[name=end_line]').val() | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?file_name=${file_name}&base_line=${end_line}&order=desc`, (data) => { | |||
| if (data.lines == 0){ | |||
| $('.header').text('您已翻阅至日志底部') | |||
| $('.message').css('display', 'block') | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| stopBubbling(arguments.callee.caller.arguments[0]) | |||
| } | |||
| function deleteVersion(version_name){ | |||
| stopBubbling(arguments.callee.caller.arguments[0]) | |||
| let flag = 1; | |||
| $('.ui.basic.modal').modal({ | |||
| onDeny: function() { | |||
| flag = false | |||
| }, | |||
| onApprove: function() { | |||
| $.post(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/del_version`,{version_name:version_name},(data)=>{ | |||
| $('#accordion'+version_name).remove() | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| flag = true | |||
| }, | |||
| onHidden: function() { | |||
| if (flag == false) { | |||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
| } | |||
| } | |||
| }) | |||
| .modal('show') | |||
| } | |||
| function stopVersion(version_name){ | |||
| stopBubbling(arguments.callee.caller.arguments[0]) | |||
| $.post(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/stop_version`,{version_name:version_name},(data)=>{ | |||
| if(data.StatusOK===0){ | |||
| $('#'+version_name+'-stop').addClass('disabled') | |||
| refreshStatus(version_name) | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| function loadLog(version_name){ | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&lines=50&order=asc`, (data) => { | |||
| $('input[name=end_line]').val(data.EndLine) | |||
| $('input[name=start_line]').val(data.StartLine) | |||
| $(`#log_file${version_name}`).text(data.Content) | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| function loadModelFile(version_name,parents,filename,init){ | |||
| parents = parents || '' | |||
| filename = filename || '' | |||
| init = init || '' | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/model_list?version_name=${version_name}&parentDir=${parents}`, (data) => { | |||
| $(`#dir_list${version_name}`).empty() | |||
| renderDir(data,version_name) | |||
| if(init==="init"){ | |||
| $(`input[name=model${version_name}]`).val("") | |||
| $(`input[name=modelback${version_name}]`).val(version_name) | |||
| $(`#file_breadcrumb${version_name}`).empty() | |||
| let htmlBread = "" | |||
| htmlBread += `<div class='active section'>${version_name}</div>` | |||
| htmlBread += "<div class='divider'> / </div>" | |||
| $(`#file_breadcrumb${version_name}`).append(htmlBread) | |||
| }else{ | |||
| renderBrend(version_name,parents,filename,init) | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err,version_name); | |||
| }); | |||
| } | |||
| function renderBrend(version_name,parents,filename,init){ | |||
| if(init=="folder"){ | |||
| let htmlBrend = "" | |||
| let sectionName=$(`#file_breadcrumb${version_name} .active.section`).text() | |||
| let parents1 = $(`input[name=model${version_name}]`).val() | |||
| let filename1 = $(`input[name=modelback${version_name}]`).val() | |||
| if(parents1===""){ | |||
| $(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','','init')">${sectionName}</a>`) | |||
| }else{ | |||
| $(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','${filename1}')">${sectionName}</a>`) | |||
| } | |||
| htmlBrend += `<div class='active section'>${filename}</div>` | |||
| htmlBrend += "<div class='divider'> / </div>" | |||
| $(`#file_breadcrumb${version_name}`).append(htmlBrend) | |||
| $(`input[name=model${version_name}]`).val(parents) | |||
| $(`input[name=modelback${version_name}]`).val(filename) | |||
| }else{ | |||
| $(`input[name=model${version_name}]`).val(parents) | |||
| $(`input[name=modelback${version_name}]`).val(filename) | |||
| $(`#file_breadcrumb${version_name} a.section:contains(${filename})`).nextAll().remove() | |||
| $(`#file_breadcrumb${version_name} a.section:contains(${filename})`).replaceWith(`<div class='active section'>${filename}</div>`) | |||
| $(`#file_breadcrumb${version_name} div.section:contains(${filename})`).append("<div class='divider'> / </div>") | |||
| } | |||
| } | |||
| function downloadModelFile(version_name,filename){ | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/model_download?version_name=${version_name}&file_name=${filename}`, (data) => { | |||
| console.log(data) | |||
| }) | |||
| } | |||
| function renderDir(data,version_name){ | |||
| let html="" | |||
| html += "<div class='ui grid' style='margin:0;'>" | |||
| html += "<div class='row' style='padding: 0;'>" | |||
| html += "<div class='ui sixteen wide column' style='padding:1rem;'>" | |||
| html += "<div class='dir list'>" | |||
| html += "<table id='repo-files-table' class='ui single line table pad20'>" | |||
| html += '<tbody>' | |||
| // html += "</tbody>" | |||
| for(let i=0;i<data.Dirs.length;i++){ | |||
| let dirs_size = renderSize(data.Dirs[i].Size) | |||
| html += "<tr>" | |||
| html += "<td class='name six wid'>" | |||
| html += "<span class='truncate'>" | |||
| html += "<span class='octicon octicon-file-directory'>" | |||
| html += "</span>" | |||
| if(data.Dirs[i].IsDir){ | |||
| html += `<a onclick="loadModelFile('${version_name}','${data.Dirs[i].ParenDir}','${data.Dirs[i].FileName}','folder')">` | |||
| html += "<span class='fitted'><i class='folder icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>" | |||
| }else{ | |||
| html += `<a href="${location.href}/model_download?version_name=${version_name}&file_name=${data.Dirs[i].FileName}&parent_dir=${data.Dirs[i].ParenDir}">` | |||
| html += "<span class='fitted'><i class='file icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>" | |||
| } | |||
| html += '</a>' | |||
| html += "</span>" | |||
| html += "</td>" | |||
| html += "<td class='message seven wide'>" | |||
| html += "<span class='truncate has-emoji'>"+ `${dirs_size}` + "</span>" | |||
| html += "</td>" | |||
| html += "<td class='text right age three wide'>" | |||
| html += "<span class='truncate has-emoji'>" + data.Dirs[i].ModTime + "</span>" | |||
| html += "</td>" | |||
| html += "</tr>" | |||
| } | |||
| html += "</tbody>" | |||
| html += "</table>" | |||
| html += "</div>" | |||
| html += "</div>" | |||
| html += "</div>" | |||
| html += "</div>" | |||
| $(`#dir_list${version_name}`).append(html) | |||
| } | |||
| function logScroll(version_name) { | |||
| let container = document.querySelector(`#log${version_name}`) | |||
| let scrollTop = container.scrollTop | |||
| let scrollHeight = container.scrollHeight | |||
| let clientHeight = container.clientHeight | |||
| if(parseInt(scrollTop) + clientHeight == scrollHeight || parseInt(scrollTop) + clientHeight +1 == scrollHeight || parseInt(scrollTop) + clientHeight - 1 == scrollHeight){ | |||
| let end_line = $(`#log${version_name} input[name=end_line]`).val() | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${end_line}&lines=50&order=desc`, (data) => { | |||
| if (data.Lines == 0){ | |||
| $(`.message${version_name} #header`).text('您已翻阅至日志底部') | |||
| $(`.message${version_name}`).css('display', 'block') | |||
| setTimeout(function(){ | |||
| $('.message').css('display', 'none') | |||
| $(`.message${version_name}`).css('display', 'none') | |||
| }, 1000) | |||
| }else{ | |||
| $('input[name=end_line]').val(data.EndLine) | |||
| $('.log').append('<pre>' + data.Content) | |||
| if(end_line===data.EndLine){ | |||
| return | |||
| } | |||
| else{ | |||
| $(`#log${version_name} input[name=end_line]`).val(data.EndLine) | |||
| $(`#log${version_name}`).append('<pre>' + data.Content) | |||
| } | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| if(scrollTop == 0){ | |||
| var start_line = $('input[name=start_line]').val() | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?file_name=${file_name}&base_line=${start_line}&order=asc`, (data) => { | |||
| if (data.lines == 0){ | |||
| $('.header').text('您已翻阅至日志顶部') | |||
| $('.message').css('display', 'block') | |||
| let start_line = $(`#log${version_name} input[name=start_line]`).val() | |||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${start_line}&lines=50&order=asc`, (data) => { | |||
| if (data.Lines == 0){ | |||
| $(`.message${version_name} #header`).text('您已翻阅至日志顶部') | |||
| $(`.message${version_name}`).css('display', 'block') | |||
| setTimeout(function(){ | |||
| $('.message').css('display', 'none') | |||
| $(`.message${version_name}`).css('display', 'none') | |||
| }, 1000) | |||
| }else{ | |||
| $('input[name=start_line]').val(data.StartLine) //如果变动就改变所对应的值 | |||
| $(".log").prepend('<pre>' + data.Content) | |||
| $(`#log${version_name} input[name=start_line]`).val(data.StartLine) //如果变动就改变所对应的值 | |||
| $(`#log${version_name}`).prepend('<pre>' + data.Content) | |||
| } | |||
| }).fail(function(err) { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| }) | |||
| } | |||
| </script> | |||
| @@ -0,0 +1,634 @@ | |||
| {{template "base/head" .}} | |||
| <style> | |||
| .unite{ | |||
| font-family: SourceHanSansSC-medium !important; | |||
| color: rgba(16, 16, 16, 100) !important; | |||
| } | |||
| .title{ | |||
| font-size: 16px !important; | |||
| padding-left: 3rem !important; | |||
| } | |||
| .min_title{ | |||
| font-size: 14px !important; | |||
| padding-left: 6rem !important; | |||
| margin-bottom: 2rem !important; | |||
| } | |||
| .width{ | |||
| width:100% !important; | |||
| } | |||
| .width80{ | |||
| width: 80.7% !important; | |||
| margin-left: 10px; | |||
| } | |||
| .width85{ | |||
| width: 85% !important; | |||
| margin-left: 4.5rem !important; | |||
| } | |||
| .width81{ | |||
| margin-left: 1.5rem; | |||
| width: 81% !important; | |||
| } | |||
| .add{font-size: 18px; | |||
| padding: 0.5rem; | |||
| border: 1px solid rgba(187, 187, 187, 100); | |||
| border-radius: 0px 5px 5px 0px; | |||
| line-height: 21px; | |||
| text-align: center; | |||
| color: #C2C7CC; | |||
| } | |||
| .min{ | |||
| font-size: 18px; | |||
| padding: 0.5rem; | |||
| border: 1px solid rgba(187, 187, 187, 100); | |||
| border-radius: 5px 0px 0px 5px; | |||
| line-height: 21px; | |||
| text-align: center; | |||
| color: #C2C7CC;" | |||
| } | |||
| #mask { | |||
| position: fixed; | |||
| top: 0px; | |||
| left: 0px; | |||
| right: 0px; | |||
| bottom: 0px; | |||
| filter: alpha(opacity=60); | |||
| background-color: #777; | |||
| z-index: 1000; | |||
| display: none; | |||
| opacity: 0.8; | |||
| -moz-opacity: 0.5; | |||
| padding-top: 100px; | |||
| color: #000000 | |||
| } | |||
| /* 加载圈css效果图 */ | |||
| #loadingPage { | |||
| margin: 200px auto; | |||
| width: 50px; | |||
| height: 40px; | |||
| text-align: center; | |||
| font-size: 10px; | |||
| display: block; | |||
| } | |||
| #loadingPage>div { | |||
| background-color: green; | |||
| height: 100%; | |||
| width: 6px; | |||
| display: inline-block; | |||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
| } | |||
| #loadingPage .rect2 { | |||
| -webkit-animation-delay: -1.1s; | |||
| animation-delay: -1.1s; | |||
| } | |||
| #loadingPage .rect3 { | |||
| -webkit-animation-delay: -1.0s; | |||
| animation-delay: -1.0s; | |||
| } | |||
| #loadingPage .rect4 { | |||
| -webkit-animation-delay: -0.9s; | |||
| animation-delay: -0.9s; | |||
| } | |||
| #loadingPage .rect5 { | |||
| -webkit-animation-delay: -0.8s; | |||
| animation-delay: -0.8s; | |||
| } | |||
| .left2{ | |||
| margin-left: -2px; | |||
| } | |||
| @-webkit-keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| -webkit-transform: scaleY(0.4) | |||
| } | |||
| 20% { | |||
| -webkit-transform: scaleY(1.0) | |||
| } | |||
| } | |||
| @keyframes sk-stretchdelay { | |||
| 0%, | |||
| 40%, | |||
| 100% { | |||
| transform: scaleY(0.4); | |||
| -webkit-transform: scaleY(0.4); | |||
| } | |||
| 20% { | |||
| transform: scaleY(1.0); | |||
| -webkit-transform: scaleY(1.0); | |||
| } | |||
| } | |||
| </style> | |||
| <!-- <div class="ui page dimmer"> | |||
| <div class="ui text loader">{{.i18n.Tr "loading"}}</div> | |||
| </div> --> | |||
| <div id="mask"> | |||
| <div id="loadingPage"> | |||
| <div class="rect1"></div> | |||
| <div class="rect2"></div> | |||
| <div class="rect3"></div> | |||
| <div class="rect4"></div> | |||
| <div class="rect5"></div> | |||
| </div> | |||
| </div> | |||
| <div class="repository"> | |||
| {{template "repo/header" .}} | |||
| <div class="ui container"> | |||
| {{template "base/alert" .}} | |||
| <h4 class="ui top attached header"> | |||
| {{.i18n.Tr "repo.modelarts.train_job.new"}} | |||
| </h4> | |||
| <div class="ui attached segment"> | |||
| <!-- equal width --> | |||
| <form class="ui form" action="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version" method="post"> | |||
| {{.CsrfTokenHtml}} | |||
| <input type="hidden" name="action" value="update"> | |||
| {{if .version_name}} | |||
| <input type="hidden" name="version_name" value="{{.version_name}}"> | |||
| {{else}} | |||
| <input type="hidden" name="version_name" value=""> | |||
| {{end}} | |||
| <input type="hidden" id="ai_engine_name" name="engine_names" value=""> | |||
| <input type="hidden" id="ai_flaver_name" name="flaver_names" value=""> | |||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
| <div class="required unite min_title inline field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
| <input type="hidden" style="width: 60%;" name="job_name" id="trainjob_job_name" value="{{.job_name}}"> | |||
| <input style="width: 60%;" value="{{.job_name}}" tabindex="3" disabled > | |||
| </div> | |||
| <div class="required unite min_title inline field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.parents_version"}}</label> | |||
| {{if .version_name}} | |||
| <input style="width: 60%;" value="{{.version_name}}" tabindex="3" disabled > | |||
| {{else}} | |||
| <input id="parents_version" style="width: 60%;" value="" tabindex="3" disabled > | |||
| {{end}} | |||
| </div> | |||
| <div class="unite min_title inline field"> | |||
| <label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}} </label> | |||
| <textarea style="width: 80%;" id="description" value="{{.description}}" name="description" rows="3" maxlength="254" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)">{{.description}}</textarea> | |||
| </div> | |||
| <div class="ui divider"></div> | |||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4> | |||
| <div class="required unite min_title inline field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label> | |||
| <select class="ui dropdown width80 left2" id="code_version" name="branch_name"> | |||
| {{if .branch_name}} | |||
| <option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option> | |||
| {{end}} | |||
| {{range $k, $v :=.branches}} | |||
| {{if ne $.branch_name $v}} | |||
| <option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="required unite min_title inline fields" style="width: 90%;"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}} </label> | |||
| <div class="field" style="flex: 1.5;"> | |||
| <select class="ui dropdown width" id="trainjob_engines" > | |||
| {{range .engines}} | |||
| <option value="{{.Value}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="field" style="flex: 2;" id="engine_name"> | |||
| <select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;' name="engine_id"> | |||
| {{if .engine_id}} | |||
| <option name="engine_id" value="{{.engine_id}}">{{.engine_name}}</option> | |||
| {{end}} | |||
| {{range .engine_versions}} | |||
| {{if ne $.engine_id .ID}} | |||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| </div> | |||
| <div class="inline unite min_title field required"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
| {{if .boot_file}} | |||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.boot_file}}" tabindex="3" autofocus required maxlength="254" > | |||
| {{else}} | |||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="254" > | |||
| {{end}} | |||
| <span> | |||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||
| </span> | |||
| </div> | |||
| <div class="required unite min_title inline field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label> | |||
| <select class="ui dropdown width80" id="trainjob_datasets" name="attachment" placeholder="选择数据集"> | |||
| {{if .dataset_name}} | |||
| <option name="attachment" value="{{.uuid}}">{{.dataset_name}}</option> | |||
| {{end}} | |||
| {{range .attachments}} | |||
| <option value="">选择数据集</option> | |||
| {{if ne $.uuid .UUID}} | |||
| <option name="attachment" value="{{.UUID}}">{{.Attachment.Name}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="inline unite min_title field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||
| <span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | |||
| <input id="store_run_para" type="hidden" name="run_para_list"> | |||
| <div class="dynamic field" style="margin-top: 1rem;"> | |||
| {{if ne 0 (len .params)}} | |||
| {{range $k ,$v := .params}} | |||
| <div class="two fields width85" id="para{{$k}}"> | |||
| <div class="field"> | |||
| <input type="text" name="shipping_first-name" value={{$v.Label}} required> | |||
| </div> | |||
| <div class="field"> | |||
| <input type="text" name="shipping_last-name" value={{$v.Value}} required> | |||
| </div> | |||
| <span> | |||
| <i class="trash icon"></i> | |||
| </span> | |||
| </div> | |||
| {{end}} | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| <div class="required field " style="display: none;"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label> | |||
| <select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id"> | |||
| {{range .resource_pools}} | |||
| <option value="{{.ID}}">{{.Value}}</option> | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="required grouped fields" style="display: none;"> | |||
| <label style="font-weight: normal;" for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label> | |||
| <div class="field"> | |||
| <div class="ui grid"> | |||
| <div class="column"> | |||
| <div class="ui radio checkbox"> | |||
| <input type="radio" name="resource_type" checked="" tabindex="0"> | |||
| </div> | |||
| </div> | |||
| <div class="three wide column">train-private-1</div> | |||
| <div class="three wide column">{{svg "octicon-verified" 16}} 运行中</div> | |||
| <div class="three wide column"> CPU:192 核 2048GiB</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="required unite min_title inline field" id="flaver_name"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||
| <select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor"> | |||
| {{if .flavor_name}} | |||
| <option name="flavor" value="{{.flavor_code}}">{{.flavor_name}}</option> | |||
| {{end}} | |||
| {{range .flavor_infos}} | |||
| {{if ne $.flavor_code .Code}} | |||
| <option name="flavor" value="{{.Code}}">{{.Value}}</option> | |||
| {{end}} | |||
| {{end}} | |||
| </select> | |||
| </div> | |||
| <div class="inline required unite min_title field"> | |||
| <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||
| <div class="ui labeled input" style="width: 5%;"> | |||
| <input style="border-radius: 0;text-align: center;" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="254" value="{{.work_server_number}}" readonly> | |||
| </div> | |||
| </div> | |||
| <div class="inline unite min_title field"> | |||
| <button class="ui create_train_job green button"> | |||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||
| </button> | |||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
| </div> | |||
| <!-- 模态框 --> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| let url_href = location.pathname.split('create_version')[0] | |||
| let url_post = location.pathname | |||
| let version_name = location.search.split('?version_name=')[1] | |||
| $("#parents_version").val(version_name) | |||
| $(".ui.button").attr('href',url_href) | |||
| $(".ui.form").attr('action',url_post) | |||
| $("input[name=version_name]").attr('value',version_name) | |||
| $('select.dropdown') | |||
| .dropdown(); | |||
| $('.menu .item') | |||
| .tab(); | |||
| let sever_num = $('#trainjob_work_server_num') | |||
| $('.add').click(function(){ | |||
| sever_num.val(parseInt(sever_num.val())+1) | |||
| if(sever_num.val()>=26){ | |||
| sever_num.val(parseInt(sever_num.val())-1) | |||
| } | |||
| }) | |||
| $('.min').click(function(){ | |||
| sever_num.val(parseInt(sever_num.val())-1) | |||
| if(sever_num.val()<=0){ | |||
| sever_num.val(parseInt(sever_num.val())+1) | |||
| } | |||
| }) | |||
| // 参数增加、删除、修改、保存 | |||
| function Add_parameter(i){ | |||
| value = '<div class="two fields width85" id= "para'+ i +'">' + | |||
| '<div class="field">' + | |||
| '<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' + | |||
| '</div> ' + | |||
| '<div class="field"> ' + | |||
| '<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' + | |||
| '</div>'+ | |||
| '<span>' + | |||
| '<i class="trash icon">' + | |||
| '</i>' + | |||
| '</span>' + | |||
| '</div>' | |||
| $(".dynamic.field").append(value) | |||
| } | |||
| $('#add_run_para').click(function(){ | |||
| var len = $(".dynamic.field .two.fields").length | |||
| Add_parameter(len) | |||
| }); | |||
| $(".dynamic.field").on("click",".trash.icon", function() { | |||
| var index = $(this).parent().parent().index() | |||
| $(this).parent().parent().remove() | |||
| var len = $(".dynamic.field .two.fields").length | |||
| $(".dynamic.field .two.fields").each(function(){ | |||
| var cur_index = $(this).index() | |||
| $(this).attr('id', 'para' + cur_index) | |||
| }) | |||
| }); | |||
| $('.ui.parameter.green.button').click(function(){ | |||
| var parameters = []; | |||
| $('table tr').each(function() { | |||
| $(this).find('td:eq(1)').each(function(){ | |||
| parameters.push($(this).text()); | |||
| }) | |||
| $(this).find('input').each(function(){ | |||
| parameters.push($(this).text()) | |||
| }) | |||
| }); | |||
| $('.ui.parameter.modal') | |||
| .modal('hide'); | |||
| for(var i = 2; i < parameters.length; i++){ | |||
| switch(i) { | |||
| // 数据集uuid待完成 | |||
| // case (2): | |||
| // console.log(1) | |||
| // break; | |||
| // $("#trainjob_datasets").val(parameters[i]); | |||
| // console.log($("#trainjob_datasets").val()) | |||
| case (3): | |||
| $("input[name='boot_file']").val(parameters[i]); | |||
| break; | |||
| case (4): | |||
| var para = parameters[i].split(" ") | |||
| for(var j = 0; j < para.length; j++){ | |||
| var para_name = para[j].split('=')[0] | |||
| var para_value = para[j].split('=')[1] | |||
| var len = $(".dynamic.field .two.fields").length | |||
| Add_parameter(len) | |||
| var pid = 'para' + len | |||
| $(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_first-name]").val(para_name) | |||
| $(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_last-name]").val(para_value) | |||
| } | |||
| break; | |||
| // 数据集pool_id待完成 | |||
| // case (5): | |||
| // $("select[name='pool_id']").val(parameters[i]); | |||
| // break; | |||
| case (6): | |||
| $("input[name='work_server_number']").val(parameters[i]); | |||
| break; | |||
| } | |||
| } | |||
| }) | |||
| $('.ui.save.checkbox').click(function(){ | |||
| $(this).checkbox({ | |||
| onChange: function(){ | |||
| if ($('.ui.save.checkbox').checkbox('is checked')){ | |||
| $('#save_para').removeClass("disabled") | |||
| }else{ | |||
| $('#save_para').addClass("disabled") | |||
| } | |||
| } | |||
| }); | |||
| }) | |||
| $('.question.circle.icon').hover(function(){ | |||
| $(this).popup('show') | |||
| }); | |||
| $(".item.active.parameter_config").click(function(){ | |||
| $('.ui.parameter.modal') | |||
| .modal('setting', 'closable', false) | |||
| .modal('show'); | |||
| }) | |||
| $('.ui.deny.button').click(function(){ | |||
| $('.ui.parameter.modal') | |||
| .modal('hide'); | |||
| }) | |||
| $('select.dropdown') | |||
| .dropdown(); | |||
| $('.ui.form') | |||
| .form({ | |||
| on: 'blur', | |||
| inline:true, | |||
| fields: { | |||
| boot_file: { | |||
| identifier : 'boot_file', | |||
| rules: [ | |||
| { | |||
| type: 'regExp[/.+\.py$/g]', | |||
| prompt : '启动文件必须为.py结尾' | |||
| } | |||
| ] | |||
| }, | |||
| job_name:{ | |||
| identifier : 'job_name', | |||
| rules: [ | |||
| { | |||
| type: 'regExp[/^[a-zA-Z0-9-_]{1,36}$/]', | |||
| prompt : '只包含大小写字母、数字、_和-,最长36个字符。' | |||
| } | |||
| ] | |||
| }, | |||
| attachment:{ | |||
| identifier : 'attachment', | |||
| rules: [ | |||
| { | |||
| type: 'empty', | |||
| prompt : '选择一个数据集' | |||
| } | |||
| ] | |||
| }, | |||
| work_server_number: { | |||
| identifier : 'work_server_number', | |||
| rules: [ | |||
| { | |||
| type : 'integer[1..25]', | |||
| prompt : '计算节点需要在1-25之间,请您键入正确的值' | |||
| } | |||
| ] | |||
| }, | |||
| run_para_list:{ | |||
| identifier : 'run_para_list', | |||
| rules: [ | |||
| { | |||
| type: 'maxLength[255]', | |||
| prompt : '所有字符最长不超过255个字符。' | |||
| } | |||
| ] | |||
| }, | |||
| }, | |||
| }) | |||
| function validate(){ | |||
| $('.ui.form') | |||
| .form({ | |||
| on: 'blur', | |||
| inline:true, | |||
| fields: { | |||
| boot_file: { | |||
| identifier : 'boot_file', | |||
| rules: [ | |||
| { | |||
| type: 'regExp[/.+\.py$/g]', | |||
| prompt : '启动文件必须为.py结尾' | |||
| } | |||
| ] | |||
| }, | |||
| job_name:{ | |||
| identifier : 'job_name', | |||
| rules: [ | |||
| { | |||
| type: 'regExp[/^[a-zA-Z0-9-_]{1,36}$/]', | |||
| prompt : '只包含大小写字母、数字、_和-,最长36个字符。' | |||
| } | |||
| ] | |||
| }, | |||
| attachment:{ | |||
| identifier : 'attachment', | |||
| rules: [ | |||
| { | |||
| type: 'empty', | |||
| prompt : '选择一个数据集' | |||
| } | |||
| ] | |||
| }, | |||
| work_server_number: { | |||
| identifier : 'work_server_number', | |||
| rules: [ | |||
| { | |||
| type : 'integer[1..25]', | |||
| prompt : '计算节点需要在1-25之间,请您键入正确的值' | |||
| } | |||
| ] | |||
| }, | |||
| run_para_list:{ | |||
| identifier : 'run_para_list', | |||
| rules: [ | |||
| { | |||
| type: 'maxLength[255]', | |||
| prompt : '所有字符最长不超过255个字符。' | |||
| } | |||
| ] | |||
| }, | |||
| }, | |||
| onSuccess: function(){ | |||
| // $('.ui.page.dimmer').dimmer('show') | |||
| document.getElementById("mask").style.display = "block" | |||
| }, | |||
| onFailure: function(e){ | |||
| return false; | |||
| } | |||
| }) | |||
| } | |||
| document.onreadystatechange = function() { | |||
| if (document.readyState === "complete") { | |||
| document.getElementById("mask").style.display = "none" | |||
| } | |||
| } | |||
| function send_run_para(){ | |||
| var run_parameters = [] | |||
| var msg = {} | |||
| $(".dynamic.field .two.fields").each(function(){ | |||
| var para_name = $(this).find('input[name=shipping_first-name]').val() | |||
| var para_value = $(this).find('input[name=shipping_last-name]').val() | |||
| run_parameters.push({"label": para_name, "value": para_value}) | |||
| }) | |||
| msg["parameter"] = run_parameters | |||
| msg = JSON.stringify(msg) | |||
| $('#store_run_para').val(msg) | |||
| } | |||
| function get_name(){ | |||
| let name1=$("#engine_name .text").text() | |||
| let name2=$("#flaver_name .text").text() | |||
| $("input#ai_engine_name").val(name1) | |||
| $("input#ai_flaver_name").val(name2) | |||
| } | |||
| $('.ui.create_train_job.green.button').click(function(e) { | |||
| get_name() | |||
| send_run_para() | |||
| validate() | |||
| }) | |||
| </script> | |||
| @@ -10,7 +10,7 @@ | |||
| </div> --> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | |||
| @@ -10,7 +10,7 @@ | |||
| </div> --> | |||
| <div class="ui two column stackable grid"> | |||
| <div class="column" style="display: flex;align-items: center;"> | |||
| <div class="ui large breadcrumb"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | |||
| @@ -53,7 +53,7 @@ | |||
| </div> | |||
| <div class="inline field {{if .Err_Description}}error{{end}}"> | |||
| <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> | |||
| <textarea id="description" name="description">{{.description}}</textarea> | |||
| <textarea id="description" name="description" maxlength="254">{{.description}}</textarea> | |||
| </div> | |||
| <div class="inline field"> | |||
| @@ -4,7 +4,11 @@ | |||
| <div class="ui container"> | |||
| {{template "base/alert" .}} | |||
| <h2 class="ui header"> | |||
| {{.i18n.Tr "repo.release.releases"}} | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="active section" href="{{.RepoLink}}/releases">{{.i18n.Tr "repo.releases"}}</div> | |||
| </div> | |||
| {{if .CanCreateRelease}} | |||
| <div class="ui right"> | |||
| <a class="ui small green button" href="{{$.RepoLink}}/releases/new"> | |||
| @@ -19,7 +19,7 @@ | |||
| {{if .PageIsEditRelease}} | |||
| <b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong> | |||
| {{else}} | |||
| <input id="tag-name" name="tag_name" value="{{.tag_name}}" placeholder="{{.i18n.Tr "repo.release.tag_name"}}" autofocus required maxlength="255"> | |||
| <input id="tag-name" name="tag_name" value="{{.tag_name}}" placeholder="{{.i18n.Tr "repo.release.tag_name"}}" autofocus required maxlength="254"> | |||
| <span class="at">@</span> | |||
| <div class="ui selection dropdown"> | |||
| <input type="hidden" name="tag_target" value="{{.tag_target}}"/> | |||
| @@ -42,7 +42,7 @@ | |||
| <div class="eleven wide column"> | |||
| <div class="field {{if .Err_Title}}error{{end}}"> | |||
| <label>{{.i18n.Tr "repo.release.title"}}</label> | |||
| <input name="title" placeholder="{{.i18n.Tr "repo.release.title"}}" value="{{.title}}" autofocus required maxlength="255"> | |||
| <input name="title" placeholder="{{.i18n.Tr "repo.release.title"}}" value="{{.title}}" autofocus required maxlength="254"> | |||
| </div> | |||
| <div class="field"> | |||
| <label>{{.i18n.Tr "repo.release.content"}}</label> | |||
| @@ -41,7 +41,7 @@ | |||
| {{end}} | |||
| <div class="field {{if .Err_Description}}error{{end}}"> | |||
| <label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label> | |||
| <textarea id="description" name="description" rows="2">{{.Repository.Description}}</textarea> | |||
| <textarea id="description" name="description" rows="2" maxlength="254">{{.Repository.Description}}</textarea> | |||
| </div> | |||
| <div class="field {{if .Err_Website}}error{{end}}"> | |||
| <label for="website">{{.i18n.Tr "repo.settings.site"}}</label> | |||
| @@ -34,6 +34,21 @@ | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| {{if .ReadmeInList}} | |||
| <div class="file-header-right"> | |||
| <div class="ui right file-actions"> | |||
| {{if .Repository.CanEnableEditor}} | |||
| {{if .CanEditFile}} | |||
| <a href="{{.RepoLink}}/_edit/{{EscapePound .BranchName}}/{{EscapePound .ReadmeName}}"><span class="btn-octicon poping up" data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-pencil" 16}}</span></a> | |||
| {{else}} | |||
| <span class="btn-octicon poping up disabled" data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-pencil" 16}}</span> | |||
| {{end}} | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| {{end}} | |||
| {{if not .ReadmeInList}} | |||
| <div class="file-header-right"> | |||
| <div class="ui right file-actions"> | |||
| @@ -2,6 +2,13 @@ | |||
| <div class="repository wiki start"> | |||
| {{template "repo/header" .}} | |||
| <div class="ui container"> | |||
| <h2 class="ui header"> | |||
| <div class="ui breadcrumb"> | |||
| <a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a> | |||
| <div class="divider"> / </div> | |||
| <div class="active section" href="{{.RepoLink}}/wiki">{{.i18n.Tr "repo.wiki"}}</div> | |||
| </div> | |||
| </h2> | |||
| <div class="ui center segment"> | |||
| {{svg "octicon-book" 32}} | |||
| <h2>{{.i18n.Tr "repo.wiki.welcome"}}</h2> | |||