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/macaron v1.4.0 | ||||
| gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | ||||
| gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 | 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/BurntSushi/toml v0.3.1 | ||||
| github.com/PuerkitoBio/goquery v1.5.0 | github.com/PuerkitoBio/goquery v1.5.0 | ||||
| github.com/RichardKnop/machinery v1.6.9 | 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/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 h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= | ||||
| gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= | 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 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | 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= | 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 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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= | ||||
| github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | 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 h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA= | ||||
| github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= | 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= | 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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | 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-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-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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/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"` | ID int64 `xorm:"pk autoincr"` | ||||
| JobID string `xorm:"INDEX NOT NULL"` | JobID string `xorm:"INDEX NOT NULL"` | ||||
| JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"` | 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 | ContainerID string | ||||
| ContainerIp string | ContainerIp string | ||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||||
| Duration int64 `xorm:"INDEX duration"` | |||||
| Duration int64 | |||||
| TrainJobDuration string | TrainJobDuration string | ||||
| DeletedAt time.Time `xorm:"deleted"` | DeletedAt time.Time `xorm:"deleted"` | ||||
| CanDebug bool `xorm:"-"` | CanDebug bool `xorm:"-"` | ||||
| CanDel 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:"-"` | User *User `xorm:"-"` | ||||
| Repo *Repository `xorm:"-"` | Repo *Repository `xorm:"-"` | ||||
| @@ -150,13 +170,16 @@ type CloudbrainsOptions struct { | |||||
| ListOptions | ListOptions | ||||
| RepoID int64 // include all repos if empty | RepoID int64 // include all repos if empty | ||||
| UserID int64 | UserID int64 | ||||
| JobID int64 | |||||
| JobID string | |||||
| SortType string | SortType string | ||||
| CloudbrainIDs []int64 | CloudbrainIDs []int64 | ||||
| // JobStatus CloudbrainStatus | // JobStatus CloudbrainStatus | ||||
| Type int | |||||
| JobType string | |||||
| Type int | |||||
| JobType string | |||||
| VersionName string | |||||
| IsLatestVersion string | |||||
| } | } | ||||
| type TaskPod struct { | type TaskPod struct { | ||||
| TaskRoleStatus struct { | TaskRoleStatus struct { | ||||
| Name string `json:"name"` | Name string `json:"name"` | ||||
| @@ -353,6 +376,7 @@ type FlavorInfos struct { | |||||
| type FlavorInfo struct { | type FlavorInfo struct { | ||||
| Id int `json:"id"` | Id int `json:"id"` | ||||
| Value string `json:"value"` | Value string `json:"value"` | ||||
| Desc string `json:"desc"` | |||||
| } | } | ||||
| type PoolInfos struct { | type PoolInfos struct { | ||||
| @@ -578,20 +602,33 @@ type Config struct { | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | ||||
| Parameter []Parameter `json:"parameter"` | Parameter []Parameter `json:"parameter"` | ||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | 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"` | //UserImageUrl string `json:"user_image_url"` | ||||
| //UserCommand string `json:"user_command"` | //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 { | type CreateConfigParams struct { | ||||
| @@ -602,20 +639,11 @@ type CreateConfigParams struct { | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | ||||
| Parameter []Parameter `json:"parameter"` | Parameter []Parameter `json:"parameter"` | ||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | 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 { | type Parameter struct { | ||||
| @@ -729,18 +757,10 @@ type GetConfigResult struct { | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | ||||
| Parameter []Parameter `json:"parameter"` | Parameter []Parameter `json:"parameter"` | ||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | 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"` | Flavor Flavor `json:"flavor"` | ||||
| PoolID string `json:"pool_id"` | PoolID string `json:"pool_id"` | ||||
| } | } | ||||
| @@ -771,25 +791,18 @@ type GetTrainJobResult struct { | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | ||||
| Parameter []Parameter `json:"parameter"` | Parameter []Parameter `json:"parameter"` | ||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | 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 { | type GetTrainJobLogResult struct { | ||||
| @@ -836,7 +849,7 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||||
| ) | ) | ||||
| } | } | ||||
| if (opts.JobID) > 0 { | |||||
| if (opts.JobID) != "" { | |||||
| cond = cond.And( | cond = cond.And( | ||||
| builder.Eq{"cloudbrain.job_id": opts.JobID}, | 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 { | if len(opts.CloudbrainIDs) > 0 { | ||||
| cond = cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | cond = cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | ||||
| @@ -891,16 +899,79 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||||
| Find(&cloudbrains); err != nil { | Find(&cloudbrains); err != nil { | ||||
| return nil, 0, fmt.Errorf("Find: %v", err) | return nil, 0, fmt.Errorf("Find: %v", err) | ||||
| } | } | ||||
| sess.Close() | |||||
| return cloudbrains, count, nil | 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) { | func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { | ||||
| if _, err = x.Insert(cloudbrain); err != nil { | if _, err = x.Insert(cloudbrain); err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -924,6 +995,16 @@ func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { | |||||
| return getRepoCloudBrain(cb) | 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) { | func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) { | ||||
| cloudBrains := make([]*Cloudbrain, 0) | cloudBrains := make([]*Cloudbrain, 0) | ||||
| err := x.Cols("job_id", "status", "type").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains) | 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 | 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 { | func UpdateJob(job *Cloudbrain) error { | ||||
| return updateJob(x, job) | return updateJob(x, job) | ||||
| } | } | ||||
| @@ -959,16 +1046,16 @@ func updateJob(e Engine, job *Cloudbrain) error { | |||||
| return err | 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 { | func DeleteJob(job *Cloudbrain) error { | ||||
| return deleteJob(x, job) | return deleteJob(x, job) | ||||
| @@ -1,6 +1,8 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "fmt" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "xorm.io/xorm" | "xorm.io/xorm" | ||||
| ) | ) | ||||
| @@ -10,10 +12,20 @@ type CustomMigration struct { | |||||
| Migrate func(*xorm.Engine) error | Migrate func(*xorm.Engine) error | ||||
| } | } | ||||
| type CustomMigrationStatic struct { | |||||
| Description string | |||||
| Migrate func(*xorm.Engine, *xorm.Engine) error | |||||
| } | |||||
| var customMigrations = []CustomMigration{ | var customMigrations = []CustomMigration{ | ||||
| {"Custom v1 Topic struct change to support chinese", syncTopicStruct}, | {"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) { | func MigrateCustom(x *xorm.Engine) { | ||||
| for _, m := range customMigrations { | 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 { | func syncTopicStruct(x *xorm.Engine) error { | ||||
| query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);" | query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);" | ||||
| @@ -34,3 +57,27 @@ func syncTopicStruct(x *xorm.Engine) error { | |||||
| _, err := x.Exec(query) | _, err := x.Exec(query) | ||||
| return err | 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 | 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) { | func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) { | ||||
| stats := &IssueStats{} | stats := &IssueStats{} | ||||
| @@ -139,6 +139,7 @@ func init() { | |||||
| new(RepoStatistic), | new(RepoStatistic), | ||||
| new(SummaryStatistic), | new(SummaryStatistic), | ||||
| new(UserBusinessAnalysis), | new(UserBusinessAnalysis), | ||||
| new(UserBusinessAnalysisAll), | |||||
| new(UserLoginLog), | new(UserLoginLog), | ||||
| ) | ) | ||||
| @@ -190,7 +191,7 @@ func setEngine(engine *xorm.Engine, table []interface{}, database *setting.DBInf | |||||
| engine.SetMaxIdleConns(setting.Database.MaxIdleConns) | engine.SetMaxIdleConns(setting.Database.MaxIdleConns) | ||||
| engine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | engine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | ||||
| engine.Sync2(table...) | engine.Sync2(table...) | ||||
| MigrateCustom(engine) | |||||
| return nil | 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 { | if err = newEngine(ctx, migrateFunc, x, tables, setting.Database); err != nil { | ||||
| return fmt.Errorf("newEngine failed: %v", err) | return fmt.Errorf("newEngine failed: %v", err) | ||||
| } | } | ||||
| MigrateCustom(x) | |||||
| xStatistic, err = getEngine(setting.DatabaseStatistic) | xStatistic, err = getEngine(setting.DatabaseStatistic) | ||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("Failed to connect to database: %v", err) | 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 { | if err = newEngine(ctx, migrateFunc, xStatistic, tablesStatistic, setting.DatabaseStatistic); err != nil { | ||||
| return fmt.Errorf("newEngine statistic failed: %v", err) | return fmt.Errorf("newEngine statistic failed: %v", err) | ||||
| } | } | ||||
| MigrateCustomStatic(x, xStatistic) | |||||
| HasEngine = true | HasEngine = true | ||||
| @@ -182,6 +182,7 @@ func CreateOrganization(org, owner *User) (err error) { | |||||
| if _, err = sess.Insert(&OrgUser{ | if _, err = sess.Insert(&OrgUser{ | ||||
| UID: owner.ID, | UID: owner.ID, | ||||
| OrgID: org.ID, | OrgID: org.ID, | ||||
| IsPublic: setting.Service.DefaultOrgMemberVisible, | |||||
| }); err != nil { | }); err != nil { | ||||
| return fmt.Errorf("insert org-user relation: %v", err) | return fmt.Errorf("insert org-user relation: %v", err) | ||||
| } | } | ||||
| @@ -210,9 +210,12 @@ type Repository struct { | |||||
| Balance string `xorm:"NOT NULL DEFAULT '0'"` | Balance string `xorm:"NOT NULL DEFAULT '0'"` | ||||
| BlockChainStatus RepoBlockChainStatus `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"` | 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"` | CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||||
| @@ -2473,6 +2476,24 @@ func (repo *Repository) IncreaseCloneCnt() { | |||||
| return | 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 { | func UpdateRepositoryCommitNum(repo *Repository) error { | ||||
| if _, err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil { | if _, err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil { | ||||
| return err | return err | ||||
| @@ -2,29 +2,43 @@ package models | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "sort" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
| ) | ) | ||||
| type ContributorWithUserId struct { | |||||
| git.Contributor | |||||
| UserId int64 | |||||
| IsAdmin bool | |||||
| RelAvatarLink string | |||||
| } | |||||
| func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | ||||
| wikiPath := "" | wikiPath := "" | ||||
| if repo.HasWiki() { | if repo.HasWiki() { | ||||
| wikiPath = repo.WikiPath() | 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{} | stats := &git.RepoKPIStats{} | ||||
| contributors, err := git.GetContributors(repoPath) | |||||
| contributors, err := git.GetContributorsDetail(repoPath, repoCreated) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| timeUntil := time.Now() | timeUntil := time.Now() | ||||
| fourMonthAgo := timeUntil.AddDate(0, -4, 0) | fourMonthAgo := timeUntil.AddDate(0, -4, 0) | ||||
| if fourMonthAgo.Before(repoCreated) { | |||||
| fourMonthAgo = repoCreated | |||||
| } | |||||
| recentlyContributors, err := git.GetContributorsDetail(repoPath, fourMonthAgo) | recentlyContributors, err := git.GetContributorsDetail(repoPath, fourMonthAgo) | ||||
| newContributersDict := make(map[string]struct{}) | newContributersDict := make(map[string]struct{}) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -34,7 +48,7 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||||
| if contributors != nil { | if contributors != nil { | ||||
| contributorDistinctDict := make(map[string]int, 0) | contributorDistinctDict := make(map[string]int, 0) | ||||
| keyContributorsDict := make(map[string]struct{}, 0) | keyContributorsDict := make(map[string]struct{}, 0) | ||||
| var commitsCount int64 | |||||
| for _, contributor := range contributors { | for _, contributor := range contributors { | ||||
| if strings.Compare(contributor.Email, "") == 0 { | if strings.Compare(contributor.Email, "") == 0 { | ||||
| continue | continue | ||||
| @@ -60,33 +74,52 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error | |||||
| setKeyContributerDict(contributorDistinctDict, contributor.Email, keyContributorsDict) | setKeyContributerDict(contributorDistinctDict, contributor.Email, keyContributorsDict) | ||||
| } | } | ||||
| commitsCount += int64(contributor.CommitCnt) | |||||
| } | } | ||||
| if recentlyContributors != nil { | if recentlyContributors != nil { | ||||
| resentlyContributorDistinctDict := make(map[string]int, 0) | |||||
| for _, recentlyContributor := range recentlyContributors { | for _, recentlyContributor := range recentlyContributors { | ||||
| user, err := GetUserByActivateEmail(recentlyContributor.Email) | user, err := GetUserByActivateEmail(recentlyContributor.Email) | ||||
| var ok bool | |||||
| if err == nil { | 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 { | } 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++ | stats.ContributorsAdded++ | ||||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| stats.Contributors = int64(len(contributorDistinctDict)) | stats.Contributors = int64(len(contributorDistinctDict)) | ||||
| stats.KeyContributors = int64(len(keyContributorsDict)) | stats.KeyContributors = int64(len(keyContributorsDict)) | ||||
| stats.Commits = int64(commitsCount) | |||||
| } | } | ||||
| err = git.SetDevelopAge(repoPath, stats) | |||||
| err = git.SetDevelopAge(repoPath, stats, repoCreated) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("FillFromGit: %v", err) | 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{}) { | func setKeyContributerDict(contributorDistinctDict map[string]int, email string, keyContributorsDict map[string]struct{}) { | ||||
| if contributorDistinctDict[email] >= 3 { | if contributorDistinctDict[email] >= 3 { | ||||
| _, ok := keyContributorsDict[email] | _, ok := keyContributorsDict[email] | ||||
| @@ -122,7 +221,8 @@ func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { | |||||
| for _, repository := range repositorys { | for _, repository := range repositorys { | ||||
| authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath()) | authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath()) | ||||
| if err1 != nil { | if err1 != nil { | ||||
| return nil, err | |||||
| log.Warn("get user kpi status err:"+repository.RepoPath(), err1.Error()) | |||||
| continue | |||||
| } | } | ||||
| for key, value := range authorsOneRepo { | 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}) | 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 | // IsCollaborator check if a user is a collaborator of a repository | ||||
| func (repo *Repository) IsCollaborator(userID int64) (bool, error) { | func (repo *Repository) IsCollaborator(userID int64) (bool, error) { | ||||
| return repo.isCollaborator(x, userID) | return repo.isCollaborator(x, userID) | ||||
| @@ -9,56 +9,58 @@ import ( | |||||
| // RepoStatistic statistic info of all repository | // RepoStatistic statistic info of all repository | ||||
| type RepoStatistic struct { | 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 { | func DeleteRepoStatDaily(date string) error { | ||||
| @@ -81,6 +83,57 @@ func DeleteRepoStatDaily(date string) error { | |||||
| return nil | 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) { | func GetRepoStatisticByDate(date string, repoId int64) ([]*RepoStatistic, error) { | ||||
| repoStatistics := make([]*RepoStatistic, 0) | repoStatistics := make([]*RepoStatistic, 0) | ||||
| err := xStatistic.Where("date = ? and repo_id=?", date, repoId).Find(&repoStatistics) | 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) | 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 { | 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=?" | 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) | _, err := xStatistic.Exec(sql, repoStat.Impact, repoStat.Completeness, repoStat.Liveness, repoStat.ProjectHealth, repoStat.TeamHealth, repoStat.Growth, repoStat.RadarTotal, repoStat.RepoID, repoStat.Date) | ||||
| return err | 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 | package models | ||||
| import ( | import ( | ||||
| "encoding/json" | |||||
| "fmt" | "fmt" | ||||
| "sort" | |||||
| "strconv" | |||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/timeutil" | "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 { | type UserBusinessAnalysis struct { | ||||
| ID int64 `xorm:"pk"` | ID int64 `xorm:"pk"` | ||||
| @@ -19,7 +89,7 @@ type UserBusinessAnalysis struct { | |||||
| //action :ActionCommitRepo // 5 | //action :ActionCommitRepo // 5 | ||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | CommitCount int `xorm:"NOT NULL DEFAULT 0"` | ||||
| //action :ActionCommentIssue // 10 | |||||
| //action :ActionCreateIssue // 6 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | IssueCount int `xorm:"NOT NULL DEFAULT 0"` | ||||
| //comment table current date | //comment table current date | ||||
| @@ -62,63 +132,260 @@ type UserBusinessAnalysis struct { | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | LoginCount int `xorm:"NOT NULL DEFAULT 0"` | ||||
| //openi index | //openi index | ||||
| OpenIIndex int `xorm:"NOT NULL DEFAULT 0"` | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | //user | ||||
| Email string `xorm:"NOT NULL"` | Email string `xorm:"NOT NULL"` | ||||
| //user | //user | ||||
| Name string `xorm:"NOT NULL"` | 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() | statictisSess := xStatistic.NewSession() | ||||
| defer statictisSess.Close() | 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) | 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) | 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 { | for _, v := range resultMap { | ||||
| userBusinessAnalysisReturnList[index] = v | |||||
| index += 1 | |||||
| userBusinessAnalysisReturnList = append(userBusinessAnalysisReturnList, v) | |||||
| } | } | ||||
| sort.Sort(userBusinessAnalysisReturnList) | |||||
| log.Info("return size=" + fmt.Sprint(len(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") | log.Info("start to count other user info data") | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| sess.Select("`user`.*").Table("user") | |||||
| sess.Select("`user`.*").Table("user").Where("type != 1 and is_active=true") | |||||
| userList := make([]*User, 0) | userList := make([]*User, 0) | ||||
| sess.Find(&userList) | 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()) | //endTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | ||||
| end_unix := endTime.Unix() | end_unix := endTime.Unix() | ||||
| CountDate := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 1, 0, 0, currentTimeNow.Location()) | 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) | 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) | CommentCountMap := queryComment(start_unix, end_unix) | ||||
| FocusRepoCountMap := queryWatch(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) | SolveIssueCountMap := querySolveIssue(start_unix, end_unix) | ||||
| CreateRepoCountMap := queryUserCreateRepo(start_unix, end_unix) | CreateRepoCountMap := queryUserCreateRepo(start_unix, end_unix) | ||||
| LoginCountMap := queryLoginCount(start_unix, end_unix) | LoginCountMap := queryLoginCount(start_unix, end_unix) | ||||
| OpenIIndexMap := queryUserRepoOpenIIndex(start_unix, end_unix) | |||||
| statictisSess := xStatistic.NewSession() | statictisSess := xStatistic.NewSession() | ||||
| defer statictisSess.Close() | defer statictisSess.Close() | ||||
| @@ -170,6 +441,7 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||||
| dateRecord.RegistDate = userRecord.CreatedUnix | dateRecord.RegistDate = userRecord.CreatedUnix | ||||
| dateRecord.Name = userRecord.Name | dateRecord.Name = userRecord.Name | ||||
| dateRecord.GiteaAgeMonth = subMonth(currentTimeNow, userRecord.CreatedUnix.AsTime()) | dateRecord.GiteaAgeMonth = subMonth(currentTimeNow, userRecord.CreatedUnix.AsTime()) | ||||
| dateRecord.DataDate = DataDate | |||||
| if _, ok := CodeMergeCountMap[dateRecord.ID]; !ok { | if _, ok := CodeMergeCountMap[dateRecord.ID]; !ok { | ||||
| dateRecord.CodeMergeCount = 0 | dateRecord.CodeMergeCount = 0 | ||||
| } else { | } else { | ||||
| @@ -248,11 +520,114 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||||
| dateRecord.LoginCount = LoginCountMap[dateRecord.ID] | dateRecord.LoginCount = LoginCountMap[dateRecord.ID] | ||||
| } | } | ||||
| if _, ok := OpenIIndexMap[dateRecord.ID]; !ok { | |||||
| dateRecord.OpenIIndex = 0 | |||||
| } else { | |||||
| dateRecord.OpenIIndex = OpenIIndexMap[dateRecord.ID] | |||||
| } | |||||
| dateRecord.CommitModelCount = 0 | 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 { | 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 | 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 { | func queryAction(start_unix int64, end_unix int64, actionType int64) map[int64]int { | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| @@ -386,10 +779,10 @@ func queryFollow(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap := make(map[int64]int) | resultMap := make(map[int64]int) | ||||
| log.Info("query Follow size=" + fmt.Sprint(len(followList))) | log.Info("query Follow size=" + fmt.Sprint(len(followList))) | ||||
| for _, followRecord := range 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 { | } else { | ||||
| resultMap[followRecord.UserID] += 1 | |||||
| resultMap[followRecord.FollowID] += 1 | |||||
| } | } | ||||
| } | } | ||||
| return resultMap | return resultMap | ||||
| @@ -432,6 +825,62 @@ func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | |||||
| return resultMap | 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 { | func queryLoginCount(start_unix int64, end_unix int64) map[int64]int { | ||||
| statictisSess := xStatistic.NewSession() | statictisSess := xStatistic.NewSession() | ||||
| defer statictisSess.Close() | defer statictisSess.Close() | ||||
| @@ -470,5 +919,8 @@ func subMonth(t1, t2 time.Time) (month int) { | |||||
| } | } | ||||
| monthInterval %= 12 | monthInterval %= 12 | ||||
| month = yearInterval*12 + monthInterval | month = yearInterval*12 + monthInterval | ||||
| if month == 0 { | |||||
| month = 1 | |||||
| } | |||||
| return month | return month | ||||
| } | } | ||||
| @@ -19,6 +19,7 @@ type CreateModelArtsNotebookForm struct { | |||||
| JobName string `form:"job_name" binding:"Required"` | JobName string `form:"job_name" binding:"Required"` | ||||
| Attachment string `form:"attachment"` | Attachment string `form:"attachment"` | ||||
| Description string `form:"description"` | Description string `form:"description"` | ||||
| Flavor string `form:"flavor"` | |||||
| } | } | ||||
| func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | 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"` | IsSaveParam string `form:"is_save_para"` | ||||
| ParameterTemplateName string `form:"parameter_template_name"` | ParameterTemplateName string `form:"parameter_template_name"` | ||||
| PrameterDescription string `form:"parameter_description"` | 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 { | 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, | Enabled: true, | ||||
| RunAtStart: false, | RunAtStart: false, | ||||
| Schedule: "@daily", | Schedule: "@daily", | ||||
| }, func(ctx context.Context, _ *models.User, _ Config) error { | }, func(ctx context.Context, _ *models.User, _ Config) error { | ||||
| repo.RepoStatisticAuto() | |||||
| repo.StatisticAuto() | |||||
| return nil | return nil | ||||
| }) | }) | ||||
| } | } | ||||
| @@ -184,16 +184,6 @@ func registerHandleSummaryStatistic() { | |||||
| return nil | 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() { | func initBasicTasks() { | ||||
| registerUpdateMirrorTask() | registerUpdateMirrorTask() | ||||
| @@ -210,7 +200,6 @@ func initBasicTasks() { | |||||
| registerHandleBlockChainMergedPulls() | registerHandleBlockChainMergedPulls() | ||||
| registerHandleBlockChainUnSuccessCommits() | registerHandleBlockChainUnSuccessCommits() | ||||
| registerHandleRepoStatistic() | |||||
| registerHandleUserStatistic() | |||||
| registerHandleRepoAndUserStatistic() | |||||
| registerHandleSummaryStatistic() | registerHandleSummaryStatistic() | ||||
| } | } | ||||
| @@ -32,7 +32,7 @@ func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (strin | |||||
| if tmpRemote != "origin" { | if tmpRemote != "origin" { | ||||
| tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base | tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base | ||||
| // Fetch commit into a temporary branch in order to be able to handle commits and tags | // 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 { | if err == nil { | ||||
| base = tmpBaseName | base = tmpBaseName | ||||
| } | } | ||||
| @@ -18,6 +18,7 @@ type RepoKPIStats struct { | |||||
| KeyContributors int64 | KeyContributors int64 | ||||
| DevelopAge int64 | DevelopAge int64 | ||||
| ContributorsAdded int64 | ContributorsAdded int64 | ||||
| Commits int64 | |||||
| CommitsAdded int64 | CommitsAdded int64 | ||||
| CommitLinesModified int64 | CommitLinesModified int64 | ||||
| WikiPages int64 | WikiPages int64 | ||||
| @@ -35,8 +36,9 @@ type UserKPITypeStats struct { | |||||
| isNewContributor bool //是否是4个月内的新增贡献者 | 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) | stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | ||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| @@ -2,6 +2,7 @@ package modelarts | |||||
| import ( | import ( | ||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | |||||
| "path" | "path" | ||||
| "strconv" | "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.4\",\"value\":\"Ascend : 4 * Ascend 910 CPU:96 核 1024GiB\"}," + | ||||
| // "{\"code\":\"modelarts.bm.910.arm.public.1\",\"value\":\"Ascend : 1 * Ascend 910 CPU:24 核 256GiB\"}" + | // "{\"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 ( | var ( | ||||
| @@ -56,19 +62,55 @@ var ( | |||||
| ) | ) | ||||
| type GenerateTrainJobReq struct { | 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 { | type VersionInfo struct { | ||||
| @@ -99,7 +141,23 @@ type ResourcePool struct { | |||||
| } `json:"resource_pool"` | } `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 | var dataActualPath string | ||||
| if uuid != "" { | if uuid != "" { | ||||
| dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + 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, | JobName: jobName, | ||||
| Description: description, | Description: description, | ||||
| ProfileID: setting.ProfileID, | ProfileID: setting.ProfileID, | ||||
| Flavor: setting.Flavor, | |||||
| Flavor: flavor, | |||||
| Pool: models.Pool{ | Pool: models.Pool{ | ||||
| ID: poolInfos.PoolInfo[0].PoolId, | ID: poolInfos.PoolInfo[0].PoolId, | ||||
| Name: poolInfos.PoolInfo[0].PoolName, | Name: poolInfos.PoolInfo[0].PoolName, | ||||
| @@ -170,14 +228,14 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||||
| return nil | return nil | ||||
| } | } | ||||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | |||||
| jobResult, err := createTrainJob(models.CreateTrainJobParams{ | jobResult, err := createTrainJob(models.CreateTrainJobParams{ | ||||
| JobName: req.JobName, | JobName: req.JobName, | ||||
| Description: req.Description, | Description: req.Description, | ||||
| Config: models.Config{ | Config: models.Config{ | ||||
| WorkServerNum: req.WorkServerNumber, | WorkServerNum: req.WorkServerNumber, | ||||
| AppUrl: req.CodeObsPath, | AppUrl: req.CodeObsPath, | ||||
| BootFileUrl: req.BootFile, | |||||
| BootFileUrl: req.BootFileUrl, | |||||
| DataUrl: req.DataUrl, | DataUrl: req.DataUrl, | ||||
| EngineID: req.EngineID, | EngineID: req.EngineID, | ||||
| TrainUrl: req.TrainUrl, | TrainUrl: req.TrainUrl, | ||||
| @@ -198,21 +256,38 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||||
| attach, err := models.GetAttachmentByUUID(req.Uuid) | attach, err := models.GetAttachmentByUUID(req.Uuid) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | ||||
| return nil | |||||
| return err | |||||
| } | } | ||||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | 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 { | if err != nil { | ||||
| @@ -223,6 +298,96 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||||
| return nil | 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 { | func TransTrainJobStatus(status int) string { | ||||
| switch status { | switch status { | ||||
| case 0: | case 0: | ||||
| @@ -273,6 +438,10 @@ func TransTrainJobStatus(status int) string { | |||||
| default: | default: | ||||
| return strconv.Itoa(status) | 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()) | 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) | 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) | return &result, fmt.Errorf("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | ||||
| } | } | ||||
| @@ -377,6 +387,61 @@ sendjob: | |||||
| return &result, nil | 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) { | func GetResourceSpecs() (*models.GetResourceSpecsResult, error) { | ||||
| checkSetting() | checkSetting() | ||||
| client := getRestyClient() | client := getRestyClient() | ||||
| @@ -768,3 +833,44 @@ sendjob: | |||||
| return &result, nil | 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" | "bytes" | ||||
| "encoding/base64" | "encoding/base64" | ||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | |||||
| "io/ioutil" | "io/ioutil" | ||||
| "net/http" | "net/http" | ||||
| @@ -113,13 +114,23 @@ func GetResultFromElk(resultInfo ResultInfo, jsonStr []byte) (loaded int, totalV | |||||
| client := &http.Client{} | client := &http.Client{} | ||||
| resp, err := client.Do(req) | resp, err := client.Do(req) | ||||
| if err != nil { | if err != nil { | ||||
| panic(err) | |||||
| // panic(err) | |||||
| return 0, 0, err | |||||
| } | } | ||||
| defer resp.Body.Close() | 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 | 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 | var timeRange Range | ||||
| timeRange.Timestamptest.Gte = Gte | timeRange.Timestamptest.Gte = Gte | ||||
| timeRange.Timestamptest.Lte = Lte | timeRange.Timestamptest.Lte = Lte | ||||
| timeRange.Timestamptest.Format = "strict_date_optional_time" | |||||
| inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Range = &timeRange | inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Range = &timeRange | ||||
| //限定用户 | //限定用户 | ||||
| var userName FilterMatchPhrase | var userName FilterMatchPhrase | ||||
| @@ -208,9 +220,10 @@ func TagNameInit(MessageInfo string, Tagname string, Gte string, Lte string) (pr | |||||
| //向elk发送请求,将获取的结果只保留访问量,输入是初始化后的数据结构,返回访问量 | //向elk发送请求,将获取的结果只保留访问量,输入是初始化后的数据结构,返回访问量 | ||||
| func ViewInfo(viewInfo InputInfo) (totalView int, err error) { | 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 jsonStr = []byte(jsons) | ||||
| var resultInfo ResultInfo | var resultInfo ResultInfo | ||||
| @@ -221,7 +234,6 @@ func ViewInfo(viewInfo InputInfo) (totalView int, err error) { | |||||
| if loaded == 0 { | if loaded == 0 { | ||||
| loaded_next, totalView, err := GetResultFromElk(resultInfo, jsonStr) | loaded_next, totalView, err := GetResultFromElk(resultInfo, jsonStr) | ||||
| time++ | time++ | ||||
| log.Info("time:", time) | |||||
| if loaded_next != 0 && time < 100 { | if loaded_next != 0 && time < 100 { | ||||
| return totalView, err | return totalView, err | ||||
| } | } | ||||
| @@ -163,6 +163,7 @@ var ( | |||||
| // UI settings | // UI settings | ||||
| UI = struct { | UI = struct { | ||||
| ExplorePagingNum int | ExplorePagingNum int | ||||
| ContributorPagingNum int | |||||
| IssuePagingNum int | IssuePagingNum int | ||||
| RepoSearchPagingNum int | RepoSearchPagingNum int | ||||
| MembersPagingNum int | MembersPagingNum int | ||||
| @@ -203,19 +204,20 @@ var ( | |||||
| Keywords string | Keywords string | ||||
| } `ini:"ui.meta"` | } `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 { | Notification: struct { | ||||
| MinTimeout time.Duration | MinTimeout time.Duration | ||||
| TimeoutStep time.Duration | TimeoutStep time.Duration | ||||
| @@ -544,7 +546,11 @@ var ( | |||||
| GrowthContributors float64 | GrowthContributors float64 | ||||
| GrowthCommit float64 | GrowthCommit float64 | ||||
| GrowthComments float64 | GrowthComments float64 | ||||
| RecordBeginTime string | |||||
| IgnoreMirrorRepo bool | |||||
| }{} | }{} | ||||
| Warn_Notify_Mails []string | |||||
| ) | ) | ||||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | // 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") | ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | ||||
| SetRadarMapConfig() | SetRadarMapConfig() | ||||
| sec = Cfg.Section("warn_mail") | |||||
| Warn_Notify_Mails = strings.Split(sec.Key("mails").MustString(""), ",") | |||||
| } | } | ||||
| func SetRadarMapConfig() { | func SetRadarMapConfig() { | ||||
| @@ -1324,6 +1333,8 @@ func SetRadarMapConfig() { | |||||
| RadarMap.GrowthContributors = sec.Key("growth_contributors").MustFloat64(0.2) | RadarMap.GrowthContributors = sec.Key("growth_contributors").MustFloat64(0.2) | ||||
| RadarMap.GrowthCommit = sec.Key("growth_commit").MustFloat64(0.2) | RadarMap.GrowthCommit = sec.Key("growth_commit").MustFloat64(0.2) | ||||
| RadarMap.GrowthComments = sec.Key("growth_comments").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 ( | import ( | ||||
| "io" | "io" | ||||
| "net/url" | |||||
| "path" | "path" | ||||
| "sort" | |||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "github.com/unknwon/com" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/obs" | "code.gitea.io/gitea/modules/obs" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "github.com/unknwon/com" | |||||
| ) | ) | ||||
| type FileInfo struct { | type FileInfo struct { | ||||
| @@ -27,20 +29,20 @@ type FileInfo struct { | |||||
| } | } | ||||
| //check if has the object | //check if has the object | ||||
| //todo:修改查询方式 | |||||
| func ObsHasObject(path string) (bool, error) { | func ObsHasObject(path string) (bool, error) { | ||||
| hasObject := false | 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 := &obs.ListObjectsInput{} | ||||
| input.Bucket = setting.Bucket | 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) | output, err := ObsCli.ListObjects(input) | ||||
| fileInfos := make([]FileInfo, 0) | fileInfos := make([]FileInfo, 0) | ||||
| if err == nil { | if err == nil { | ||||
| for _, val := range output.Contents { | for _, val := range output.Contents { | ||||
| str1 := strings.Split(val.Key, "/") | str1 := strings.Split(val.Key, "/") | ||||
| var isDir bool | var isDir bool | ||||
| var fileName,nextParentDir string | |||||
| var fileName, nextParentDir string | |||||
| if strings.HasSuffix(val.Key, "/") { | if strings.HasSuffix(val.Key, "/") { | ||||
| //dirs in next level dir | |||||
| if len(str1)-len(strPrefix) > 2 { | |||||
| continue | |||||
| } | |||||
| fileName = str1[len(str1)-2] | fileName = str1[len(str1)-2] | ||||
| isDir = true | 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 | continue | ||||
| } | } | ||||
| } else { | } else { | ||||
| //files in next level dir | |||||
| if len(str1)-len(strPrefix) > 1 { | |||||
| continue | |||||
| } | |||||
| fileName = str1[len(str1)-1] | fileName = str1[len(str1)-1] | ||||
| isDir = false | isDir = false | ||||
| nextParentDir = parentDir | |||||
| } | } | ||||
| fileInfo := FileInfo{ | 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, | FileName: fileName, | ||||
| Size: val.Size, | |||||
| IsDir:isDir, | |||||
| Size: val.Size, | |||||
| IsDir: isDir, | |||||
| ParenDir: nextParentDir, | ParenDir: nextParentDir, | ||||
| } | } | ||||
| fileInfos = append(fileInfos, fileInfo) | fileInfos = append(fileInfos, fileInfo) | ||||
| } | } | ||||
| sort.Slice(fileInfos, func(i, j int) bool { | |||||
| return fileInfos[i].ModTime > fileInfos[j].ModTime | |||||
| }) | |||||
| return fileInfos, err | return fileInfos, err | ||||
| } else { | } else { | ||||
| if obsError, ok := err.(obs.ObsError); ok { | if obsError, ok := err.(obs.ObsError); ok { | ||||
| @@ -242,11 +262,12 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) | |||||
| input := &obs.CreateSignedUrlInput{} | input := &obs.CreateSignedUrlInput{} | ||||
| input.Bucket = setting.Bucket | input.Bucket = setting.Bucket | ||||
| input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/") | input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/") | ||||
| input.Expires = 60 * 60 | input.Expires = 60 * 60 | ||||
| input.Method = obs.HttpMethodGet | input.Method = obs.HttpMethodGet | ||||
| reqParams := make(map[string]string) | reqParams := make(map[string]string) | ||||
| fileName = url.QueryEscape(fileName) | |||||
| reqParams["response-content-disposition"] = "attachment; filename=\"" + fileName + "\"" | reqParams["response-content-disposition"] = "attachment; filename=\"" + fileName + "\"" | ||||
| input.QueryParams = reqParams | input.QueryParams = reqParams | ||||
| output, err := ObsCli.CreateSignedUrl(input) | output, err := ObsCli.CreateSignedUrl(input) | ||||
| @@ -254,8 +275,34 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) | |||||
| log.Error("CreateSignedUrl failed:", err.Error()) | log.Error("CreateSignedUrl failed:", err.Error()) | ||||
| return "", err | 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 | return output.SignedUrl, nil | ||||
| } | } | ||||
| func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | ||||
| @@ -92,6 +92,7 @@ func NewFuncMap() []template.FuncMap { | |||||
| "Str2html": Str2html, | "Str2html": Str2html, | ||||
| "TimeSince": timeutil.TimeSince, | "TimeSince": timeutil.TimeSince, | ||||
| "TimeSinceUnix": timeutil.TimeSinceUnix, | "TimeSinceUnix": timeutil.TimeSinceUnix, | ||||
| "TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||||
| "RawTimeSince": timeutil.RawTimeSince, | "RawTimeSince": timeutil.RawTimeSince, | ||||
| "FileSize": base.FileSize, | "FileSize": base.FileSize, | ||||
| "PrettyNumber": base.PrettyNumber, | "PrettyNumber": base.PrettyNumber, | ||||
| @@ -340,6 +341,7 @@ func NewTextFuncMap() []texttmpl.FuncMap { | |||||
| }, | }, | ||||
| "TimeSince": timeutil.TimeSince, | "TimeSince": timeutil.TimeSince, | ||||
| "TimeSinceUnix": timeutil.TimeSinceUnix, | "TimeSinceUnix": timeutil.TimeSinceUnix, | ||||
| "TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||||
| "RawTimeSince": timeutil.RawTimeSince, | "RawTimeSince": timeutil.RawTimeSince, | ||||
| "DateFmtLong": func(t time.Time) string { | "DateFmtLong": func(t time.Time) string { | ||||
| return t.Format(time.RFC1123Z) | return t.Format(time.RFC1123Z) | ||||
| @@ -162,3 +162,8 @@ func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML { | |||||
| then.FormatInLocation(GetTimeFormat(lang), setting.DefaultUILocation), | then.FormatInLocation(GetTimeFormat(lang), setting.DefaultUILocation), | ||||
| timeSinceUnix(int64(then), int64(now), lang))) | 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 | show_only_public = Showing only public | ||||
| issues.in_your_repos = In your repositories | issues.in_your_repos = In your repositories | ||||
| contributors = Contributors | |||||
| [explore] | [explore] | ||||
| repos = Repositories | 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_pattern_not_allowed = The pattern '%s' is not allowed in a username. | ||||
| form.name_chars_not_allowed = User name '%s' contains invalid characters. | 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] | [settings] | ||||
| profile = Profile | profile = Profile | ||||
| account = Account | account = Account | ||||
| @@ -755,6 +776,7 @@ unit_disabled = The site administrator has disabled this repository section. | |||||
| language_other = Other | language_other = Other | ||||
| datasets = Datasets | datasets = Datasets | ||||
| datasets.desc = Enable Dataset | datasets.desc = Enable Dataset | ||||
| cloudbrain_helper=Use GPU/NPU resources to open notebooks, model training tasks, etc. | |||||
| debug=Debug | debug=Debug | ||||
| stop=Stop | stop=Stop | ||||
| @@ -785,7 +807,15 @@ cloudbrain_operate = Operate | |||||
| cloudbrain_status_createtime = Status/Createtime | cloudbrain_status_createtime = Status/Createtime | ||||
| cloudbrain_status_runtime = Running Time | 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.notebook=Debug Task | ||||
| modelarts.train_job=Train Task | modelarts.train_job=Train Task | ||||
| modelarts.train_job.new_debug= New Debug 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.config=Configuration information | ||||
| modelarts.train_job.new=New train Task | modelarts.train_job.new=New train Task | ||||
| modelarts.train_job.new_place=The description should not exceed 256 characters | 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 | 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.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.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.train_job.dataset=Dataset | ||||
| modelarts.code_version = Code Version | |||||
| modelarts.parents_version = Parents Version | |||||
| modelarts.train_job.run_parameter=Run Parameter | modelarts.train_job.run_parameter=Run Parameter | ||||
| modelarts.train_job.add_run_parameter=Add Run Parameter | modelarts.train_job.add_run_parameter=Add Run Parameter | ||||
| modelarts.train_job.parameter_name=Parameter Name | modelarts.train_job.parameter_name=Parameter Name | ||||
| @@ -2152,6 +2188,19 @@ repos.stars = Stars | |||||
| repos.forks = Forks | repos.forks = Forks | ||||
| repos.issues = Issues | repos.issues = Issues | ||||
| repos.size = Size | 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.dataset_manage_panel= Dataset Manage | ||||
| datasets.owner=Owner | datasets.owner=Owner | ||||
| @@ -220,6 +220,8 @@ show_only_public=只显示公开的 | |||||
| issues.in_your_repos=属于该用户项目的 | issues.in_your_repos=属于该用户项目的 | ||||
| contributors=贡献者 | |||||
| [explore] | [explore] | ||||
| repos=项目 | repos=项目 | ||||
| users=用户 | users=用户 | ||||
| @@ -227,6 +229,7 @@ organizations=组织 | |||||
| images = 云脑镜像 | images = 云脑镜像 | ||||
| search=搜索 | search=搜索 | ||||
| code=代码 | code=代码 | ||||
| data_analysis=数字看板(内测) | |||||
| repo_no_results=未找到匹配的项目。 | repo_no_results=未找到匹配的项目。 | ||||
| dataset_no_results = 未找到匹配的数据集。 | dataset_no_results = 未找到匹配的数据集。 | ||||
| user_no_results=未找到匹配的用户。 | user_no_results=未找到匹配的用户。 | ||||
| @@ -402,6 +405,25 @@ form.name_reserved='%s' 用户名被保留。 | |||||
| form.name_pattern_not_allowed=用户名中不允许使用 "%s"。 | form.name_pattern_not_allowed=用户名中不允许使用 "%s"。 | ||||
| form.name_chars_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] | [settings] | ||||
| profile=个人信息 | profile=个人信息 | ||||
| account=账号 | account=账号 | ||||
| @@ -757,6 +779,7 @@ unit_disabled=站点管理员已禁用此项目单元。 | |||||
| language_other=其它 | language_other=其它 | ||||
| datasets=数据集 | datasets=数据集 | ||||
| datasets.desc=数据集功能 | datasets.desc=数据集功能 | ||||
| cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等 | |||||
| debug=调试 | debug=调试 | ||||
| stop=停止 | stop=停止 | ||||
| @@ -787,13 +810,31 @@ cloudbrain_status_createtime=状态/创建时间 | |||||
| cloudbrain_status_runtime = 运行时长 | cloudbrain_status_runtime = 运行时长 | ||||
| cloudbrain_jobname_err=只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。 | 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.notebook=调试任务 | ||||
| modelarts.train_job=训练任务 | modelarts.train_job=训练任务 | ||||
| modelarts.train_job.new_debug=新建调试任务 | modelarts.train_job.new_debug=新建调试任务 | ||||
| modelarts.train_job.new_train=新建训练任务 | modelarts.train_job.new_train=新建训练任务 | ||||
| modelarts.train_job.config=配置信息 | modelarts.train_job.config=配置信息 | ||||
| modelarts.train_job.new=新建训练任务 | 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.algorithm_origin=算法来源 | ||||
| modelarts.train_job.AI_driver=AI引擎 | modelarts.train_job.AI_driver=AI引擎 | ||||
| modelarts.train_job.start_file=启动文件 | 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.boot_file_place=填写启动文件路径,默认为train.py | ||||
| modelarts.train_job.dataset=数据集 | 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.run_parameter=运行参数 | ||||
| modelarts.train_job.add_run_parameter=增加运行参数 | modelarts.train_job.add_run_parameter=增加运行参数 | ||||
| modelarts.train_job.parameter_name=参数名 | modelarts.train_job.parameter_name=参数名 | ||||
| @@ -2153,6 +2199,19 @@ repos.stars=点赞数 | |||||
| repos.forks=派生数 | repos.forks=派生数 | ||||
| repos.issues=任务数 | repos.issues=任务数 | ||||
| repos.size=大小 | 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.dataset_manage_panel=数据集管理 | ||||
| datasets.owner=所有者 | datasets.owner=所有者 | ||||
| @@ -19,11 +19,13 @@ | |||||
| "cssnano": "4.1.10", | "cssnano": "4.1.10", | ||||
| "domino": "2.1.5", | "domino": "2.1.5", | ||||
| "dropzone": "5.7.2", | "dropzone": "5.7.2", | ||||
| "echarts": "3.8.5", | |||||
| "element-ui": "2.15.5", | "element-ui": "2.15.5", | ||||
| "esdk-obs-browserjs": "3.20.7", | "esdk-obs-browserjs": "3.20.7", | ||||
| "esdk-obs-nodejs": "3.20.11", | "esdk-obs-nodejs": "3.20.11", | ||||
| "fast-glob": "3.2.2", | "fast-glob": "3.2.2", | ||||
| "file-loader": "6.0.0", | "file-loader": "6.0.0", | ||||
| "file-saver": "2.0.5", | |||||
| "fomantic-ui": "2.8.4", | "fomantic-ui": "2.8.4", | ||||
| "fs": "0.0.1-security", | "fs": "0.0.1-security", | ||||
| "highlight.js": "10.4.1", | "highlight.js": "10.4.1", | ||||
| @@ -55,13 +57,15 @@ | |||||
| "webpack": "4.43.0", | "webpack": "4.43.0", | ||||
| "webpack-cli": "3.3.11", | "webpack-cli": "3.3.11", | ||||
| "webpack-fix-style-only-entries": "0.4.0", | "webpack-fix-style-only-entries": "0.4.0", | ||||
| "worker-loader": "2.0.0" | |||||
| "worker-loader": "2.0.0", | |||||
| "xlsx": "0.17.3" | |||||
| }, | }, | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "eslint": "6.8.0", | "eslint": "6.8.0", | ||||
| "eslint-config-airbnb-base": "14.1.0", | "eslint-config-airbnb-base": "14.1.0", | ||||
| "eslint-plugin-import": "2.20.2", | "eslint-plugin-import": "2.20.2", | ||||
| "eslint-plugin-vue": "6.2.2", | "eslint-plugin-vue": "6.2.2", | ||||
| "script-loader": "0.7.2", | |||||
| "stylelint": "13.3.3", | "stylelint": "13.3.3", | ||||
| "stylelint-config-standard": "20.0.0", | "stylelint-config-standard": "20.0.0", | ||||
| "updates": "10.2.11" | "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). | Get(notify.GetThread). | ||||
| Patch(notify.ReadThread) | Patch(notify.ReadThread) | ||||
| }, reqToken()) | }, 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 | // Users | ||||
| m.Group("/users", func() { | m.Group("/users", func() { | ||||
| @@ -858,8 +875,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| }) | }) | ||||
| m.Group("/train-job", func() { | m.Group("/train-job", func() { | ||||
| m.Group("/:jobid", func() { | m.Group("/:jobid", func() { | ||||
| m.Get("", repo.GetModelArtsTrainJob) | |||||
| m.Get("", repo.GetModelArtsTrainJobVersion) | |||||
| m.Get("/log", repo.TrainJobGetLog) | 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)) | }, reqRepoReader(models.UnitTypeCloudBrain)) | ||||
| @@ -6,12 +6,15 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "net/http" | |||||
| "strconv" | |||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/modelarts" | "code.gitea.io/gitea/modules/modelarts" | ||||
| "net/http" | |||||
| "strconv" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| ) | ) | ||||
| func GetModelArtsNotebook(ctx *context.APIContext) { | func GetModelArtsNotebook(ctx *context.APIContext) { | ||||
| @@ -72,56 +75,247 @@ func GetModelArtsTrainJob(ctx *context.APIContext) { | |||||
| } | } | ||||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | 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 ( | var ( | ||||
| err error | 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 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 baseLine = ctx.Query("base_line") | ||||
| var order = ctx.Query("order") | 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 { | if order != modelarts.OrderDesc && order != modelarts.OrderAsc { | ||||
| log.Error("order(%s) check failed", order) | log.Error("order(%s) check failed", order) | ||||
| ctx.JSON(http.StatusBadRequest, map[string]interface{}{ | ctx.JSON(http.StatusBadRequest, map[string]interface{}{ | ||||
| "err_msg": "order check failed", | |||||
| "err_msg": "order check failed", | |||||
| }) | }) | ||||
| return | return | ||||
| } | } | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| resultLogFile, result, err := trainJobGetLogContent(jobID, versionName, baseLine, order, lines_int) | |||||
| if err != nil { | 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 | 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 { | if err != nil { | ||||
| log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) | 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 | return | ||||
| } | } | ||||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | 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 explore organizations page template | ||||
| tplExploreOrganizations base.TplName = "explore/organizations" | tplExploreOrganizations base.TplName = "explore/organizations" | ||||
| // tplExploreCode explore code page template | // 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 | // Home render home page | ||||
| @@ -146,7 +147,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { | |||||
| ctx.Data["SortType"] = "hot" | ctx.Data["SortType"] = "hot" | ||||
| orderBy = models.SearchOrderByHot | orderBy = models.SearchOrderByHot | ||||
| } | } | ||||
| orderBy = orderBy + ",id" | |||||
| //todo:support other topics | //todo:support other topics | ||||
| keyword := strings.Trim(ctx.Query("q"), " ") | keyword := strings.Trim(ctx.Query("q"), " ") | ||||
| topic := strings.Trim(ctx.Query("topic"), " ") | topic := strings.Trim(ctx.Query("topic"), " ") | ||||
| @@ -501,6 +502,10 @@ func ExploreImages(ctx *context.Context) { | |||||
| ctx.HTML(200, tplExploreImages) | ctx.HTML(200, tplExploreImages) | ||||
| } | } | ||||
| func ExploreDataAnalysis(ctx *context.Context) { | |||||
| ctx.HTML(200, tplExploreExploreDataAnalysis) | |||||
| } | |||||
| // NotFound render 404 page | // NotFound render 404 page | ||||
| func NotFound(ctx *context.Context) { | func NotFound(ctx *context.Context) { | ||||
| ctx.Data["Title"] = "Page Not Found" | ctx.Data["Title"] = "Page Not Found" | ||||
| @@ -43,7 +43,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/manager/restart", Restart) | m.Post("/manager/restart", Restart) | ||||
| m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) | m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) | ||||
| m.Post("/tool/update_all_repo_commit_cnt", UpdateAllRepoCommitCnt) | 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) | }, CheckInternalToken) | ||||
| } | } | ||||
| @@ -39,8 +39,35 @@ func UpdateAllRepoCommitCnt(ctx *macaron.Context) { | |||||
| } | } | ||||
| func RepoStatisticManually(ctx *macaron.Context) { | func RepoStatisticManually(ctx *macaron.Context) { | ||||
| date := ctx.Query("date") | |||||
| date := ctx.Params("date") | |||||
| repo.RepoStatisticDaily(date) | repo.RepoStatisticDaily(date) | ||||
| repo.SummaryStatisticDaily(date) | repo.SummaryStatisticDaily(date) | ||||
| repo.TimingCountDataByDate(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 | // AddAttachment response for add attachment record | ||||
| func AddAttachment(ctx *context.Context) { | func AddAttachment(ctx *context.Context) { | ||||
| typeCloudBrain := ctx.QueryInt("type") | typeCloudBrain := ctx.QueryInt("type") | ||||
| fileName := ctx.Query("file_name") | |||||
| err := checkTypeCloudBrain(typeCloudBrain) | err := checkTypeCloudBrain(typeCloudBrain) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("checkTypeCloudBrain failed", err) | ctx.ServerError("checkTypeCloudBrain failed", err) | ||||
| @@ -375,7 +376,7 @@ func AddAttachment(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| } else { | } else { | ||||
| has, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(uuid) + "/" + uuid) | |||||
| has, err = storage.ObsHasObject(setting.BasePath + models.AttachmentRelativePath(uuid) + "/" + fileName) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("ObsHasObject", err) | ctx.ServerError("ObsHasObject", err) | ||||
| return | return | ||||
| @@ -391,7 +392,7 @@ func AddAttachment(ctx *context.Context) { | |||||
| UUID: uuid, | UUID: uuid, | ||||
| UploaderID: ctx.User.ID, | UploaderID: ctx.User.ID, | ||||
| IsPrivate: true, | IsPrivate: true, | ||||
| Name: ctx.Query("file_name"), | |||||
| Name: fileName, | |||||
| Size: ctx.QueryInt64("size"), | Size: ctx.QueryInt64("size"), | ||||
| DatasetID: ctx.QueryInt64("dataset_id"), | DatasetID: ctx.QueryInt64("dataset_id"), | ||||
| Type: typeCloudBrain, | Type: typeCloudBrain, | ||||
| @@ -479,6 +480,7 @@ func UpdateAttachmentDecompressState(ctx *context.Context) { | |||||
| func GetSuccessChunks(ctx *context.Context) { | func GetSuccessChunks(ctx *context.Context) { | ||||
| fileMD5 := ctx.Query("md5") | fileMD5 := ctx.Query("md5") | ||||
| typeCloudBrain := ctx.QueryInt("type") | typeCloudBrain := ctx.QueryInt("type") | ||||
| fileName := ctx.Query("file_name") | |||||
| var chunks string | var chunks string | ||||
| err := checkTypeCloudBrain(typeCloudBrain) | err := checkTypeCloudBrain(typeCloudBrain) | ||||
| @@ -510,7 +512,7 @@ func GetSuccessChunks(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| } else { | } 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 { | if err != nil { | ||||
| ctx.ServerError("ObsHasObject failed", err) | ctx.ServerError("ObsHasObject failed", err) | ||||
| return | return | ||||
| @@ -10,6 +10,7 @@ import ( | |||||
| "os" | "os" | ||||
| "os/exec" | "os/exec" | ||||
| "regexp" | "regexp" | ||||
| "sort" | |||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| @@ -199,12 +200,12 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| command := form.Command | command := form.Command | ||||
| uuid := form.Attachment | uuid := form.Attachment | ||||
| jobType := form.JobType | jobType := form.JobType | ||||
| gpuQueue := setting.JobType | |||||
| gpuQueue := form.GpuType | |||||
| codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | ||||
| resourceSpecId := form.ResourceSpecId | resourceSpecId := form.ResourceSpecId | ||||
| if !jobNamePattern.MatchString(jobName) { | 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 | return | ||||
| } | } | ||||
| @@ -242,7 +243,6 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| benchmarkPath := setting.JobPath + jobName + cloudbrain.BenchMarkMountPath | benchmarkPath := setting.JobPath + jobName + cloudbrain.BenchMarkMountPath | ||||
| if setting.IsBenchmarkEnabled && jobType == string(models.JobTypeBenchmark) { | if setting.IsBenchmarkEnabled && jobType == string(models.JobTypeBenchmark) { | ||||
| gpuQueue = form.GpuType | |||||
| var gpuType string | var gpuType string | ||||
| for _, gpuInfo := range gpuInfos.GpuInfo { | for _, gpuInfo := range gpuInfos.GpuInfo { | ||||
| if gpuInfo.Queue == gpuQueue { | if gpuInfo.Queue == gpuQueue { | ||||
| @@ -528,6 +528,15 @@ func CloudBrainShowModels(ctx *context.Context) { | |||||
| return | 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["Path"] = dirArray | ||||
| ctx.Data["Dirs"] = fileInfos | ctx.Data["Dirs"] = fileInfos | ||||
| ctx.Data["task"] = task | ctx.Data["task"] = task | ||||
| @@ -317,6 +317,12 @@ func HTTP(ctx *context.Context) { | |||||
| go repo.IncreaseCloneCnt() | go repo.IncreaseCloneCnt() | ||||
| } | } | ||||
| if ctx.Req.Method == "POST" { | |||||
| if strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") { | |||||
| go repo.IncreaseGitCloneCnt() | |||||
| } | |||||
| } | |||||
| w := ctx.Resp | w := ctx.Resp | ||||
| r := ctx.Req.Request | r := ctx.Req.Request | ||||
| cfg := &serviceConfig{ | cfg := &serviceConfig{ | ||||
| @@ -1,20 +1,26 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "errors" | |||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/normalization" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/normalization" | |||||
| "code.gitea.io/gitea/modules/repository" | "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() { | func RepoStatisticAuto() { | ||||
| log.Info("", time.Now()) | |||||
| yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | ||||
| setting.UpdateRadarMap() | setting.UpdateRadarMap() | ||||
| RepoStatisticDaily(yesterday) | RepoStatisticDaily(yesterday) | ||||
| @@ -24,14 +30,17 @@ func RepoStatisticDaily(date string) { | |||||
| log.Info("%s", date) | log.Info("%s", date) | ||||
| log.Info("begin Repo Statistic") | log.Info("begin Repo Statistic") | ||||
| t, _ := time.Parse("2006-01-02", date) | t, _ := time.Parse("2006-01-02", date) | ||||
| warnEmailMessage := "项目统计信息入库失败,请尽快定位。" | |||||
| if err := models.DeleteRepoStatDaily(date); err != nil { | if err := models.DeleteRepoStatDaily(date); err != nil { | ||||
| log.Error("DeleteRepoStatDaily failed: %v", err.Error()) | log.Error("DeleteRepoStatDaily failed: %v", err.Error()) | ||||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||||
| return | return | ||||
| } | } | ||||
| repos, err := models.GetAllRepositories() | repos, err := models.GetAllRepositories() | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetAllRepositories failed: %v", err.Error()) | log.Error("GetAllRepositories failed: %v", err.Error()) | ||||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||||
| return | return | ||||
| } | } | ||||
| @@ -40,12 +49,14 @@ func RepoStatisticDaily(date string) { | |||||
| var minRepoRadar models.RepoStatistic | var minRepoRadar models.RepoStatistic | ||||
| var maxRepoRadar 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) | repoGitStat, err := models.GetRepoKPIStats(repo) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetRepoKPIStats failed: %s", repo.Name) | |||||
| log.Error("GetRepoKPIStats failed: %s", getDistinctProjectName(repo)) | |||||
| } else { | } else { | ||||
| numDevMonths = repoGitStat.DevelopAge | numDevMonths = repoGitStat.DevelopAge | ||||
| numKeyContributor = repoGitStat.KeyContributors | numKeyContributor = repoGitStat.KeyContributors | ||||
| @@ -54,36 +65,40 @@ func RepoStatisticDaily(date string) { | |||||
| numCommitsGrowth = repoGitStat.CommitsAdded | numCommitsGrowth = repoGitStat.CommitsAdded | ||||
| numCommitLinesGrowth = repoGitStat.CommitLinesModified | numCommitLinesGrowth = repoGitStat.CommitLinesModified | ||||
| numContributorsGrowth = repoGitStat.ContributorsAdded | numContributorsGrowth = repoGitStat.ContributorsAdded | ||||
| numCommits = repoGitStat.Commits | |||||
| } | } | ||||
| var issueFixedRate float32 | var issueFixedRate float32 | ||||
| if repo.NumIssues != 0 { | if repo.NumIssues != 0 { | ||||
| issueFixedRate = float32(repo.NumClosedIssues) / float32(repo.NumIssues) | issueFixedRate = float32(repo.NumClosedIssues) / float32(repo.NumIssues) | ||||
| } else { | |||||
| issueFixedRate = 1.0 | |||||
| } | } | ||||
| var numVersions int64 | var numVersions int64 | ||||
| numVersions, err = models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{}) | numVersions, err = models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{}) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetReleaseCountByRepoID failed(%s): %v", repo.Name, err) | |||||
| log.Error("GetReleaseCountByRepoID failed(%s): %v", getDistinctProjectName(repo), err) | |||||
| } | } | ||||
| var datasetSize int64 | var datasetSize int64 | ||||
| datasetSize, err = getDatasetSize(repo) | datasetSize, err = getDatasetSize(repo) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("getDatasetSize failed(%s): %v", repo.Name, err) | |||||
| log.Error("getDatasetSize failed(%s): %v", getDistinctProjectName(repo), err) | |||||
| } | } | ||||
| var numComments int64 | var numComments int64 | ||||
| numComments, err = models.GetCommentCountByRepoID(repo.ID) | numComments, err = models.GetCommentCountByRepoID(repo.ID) | ||||
| if err != nil { | 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) | beginTime, endTime := getStatTime(date) | ||||
| var numVisits int | var numVisits int | ||||
| numVisits, err = repository.AppointProjectView(repo.OwnerName, repo.Name, beginTime, endTime) | numVisits, err = repository.AppointProjectView(repo.OwnerName, repo.Name, beginTime, endTime) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("AppointProjectView failed(%s): %v", repo.Name, err) | |||||
| log.Error("AppointProjectView failed(%s): %v", getDistinctProjectName(repo), err) | |||||
| } | } | ||||
| repoStat := models.RepoStatistic{ | repoStat := models.RepoStatistic{ | ||||
| @@ -91,8 +106,11 @@ func RepoStatisticDaily(date string) { | |||||
| Date: date, | Date: date, | ||||
| Name: repo.Name, | Name: repo.Name, | ||||
| IsPrivate: repo.IsPrivate, | IsPrivate: repo.IsPrivate, | ||||
| IsMirror: repo.IsMirror, | |||||
| OwnerName: repo.OwnerName, | |||||
| NumWatches: int64(repo.NumWatches), | NumWatches: int64(repo.NumWatches), | ||||
| NumStars: int64(repo.NumStars), | NumStars: int64(repo.NumStars), | ||||
| NumForks: int64(repo.NumForks), | |||||
| NumDownloads: repo.CloneCnt, | NumDownloads: repo.CloneCnt, | ||||
| NumComments: numComments, | NumComments: numComments, | ||||
| NumVisits: int64(numVisits), | NumVisits: int64(numVisits), | ||||
| @@ -103,7 +121,7 @@ func RepoStatisticDaily(date string) { | |||||
| DatasetSize: datasetSize, | DatasetSize: datasetSize, | ||||
| NumModels: 0, | NumModels: 0, | ||||
| NumWikiViews: numWikiViews, | NumWikiViews: numWikiViews, | ||||
| NumCommits: repo.NumCommit, | |||||
| NumCommits: numCommits, | |||||
| NumIssues: int64(repo.NumIssues), | NumIssues: int64(repo.NumIssues), | ||||
| NumPulls: int64(repo.NumPulls), | NumPulls: int64(repo.NumPulls), | ||||
| IssueFixedRate: issueFixedRate, | IssueFixedRate: issueFixedRate, | ||||
| @@ -144,14 +162,16 @@ func RepoStatisticDaily(date string) { | |||||
| } | } | ||||
| if _, err = models.InsertRepoStat(&repoStat); err != nil { | 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 | continue | ||||
| } | } | ||||
| tempRepoStat := models.RepoStatistic{ | tempRepoStat := models.RepoStatistic{ | ||||
| RepoID: repoStat.RepoID, | RepoID: repoStat.RepoID, | ||||
| Date: repoStat.Date, | Date: repoStat.Date, | ||||
| IsMirror: repoStat.IsMirror, | |||||
| Impact: normalization.GetImpactInitValue(repoStat.NumWatches, repoStat.NumStars, repoStat.NumForks, repoStat.NumDownloads, repoStat.NumComments, repoStat.NumVisits), | 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), | 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), | Liveness: normalization.GetLivenessInitValue(repoStat.NumCommits, repoStat.NumIssues, repoStat.NumPulls, repoStat.NumVisits), | ||||
| @@ -162,74 +182,91 @@ func RepoStatisticDaily(date string) { | |||||
| reposRadar = append(reposRadar, &tempRepoStat) | 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 | //radar map | ||||
| log.Info("begin statistic radar") | log.Info("begin statistic radar") | ||||
| for _, radarInit := range reposRadar { | 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) | 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) { | func getDatasetSize(repo *models.Repository) (int64, error) { | ||||
| dataset, err := models.GetDatasetByRepo(repo) | dataset, err := models.GetDatasetByRepo(repo) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -257,3 +298,29 @@ func getStatTime(timeStr string) (string, string) { | |||||
| return beginTime, endTime | 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 | package repo | ||||
| import ( | import ( | ||||
| "fmt" | |||||
| "net/http" | |||||
| "net/url" | |||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | |||||
| "code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
| "code.gitea.io/gitea/modules/log" | "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) | t, _ := time.Parse("2006-01-02", date) | ||||
| startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | 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 | //query wiki data | ||||
| log.Info("start to time count data") | log.Info("start to time count data") | ||||
| wikiMap := make(map[string]int) | wikiMap := make(map[string]int) | ||||
| warnEmailMessage := "用户统计信息入库失败,请尽快定位。" | |||||
| repoList, err := models.GetAllRepositories() | repoList, err := models.GetAllRepositories() | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("query repo error.") | |||||
| log.Error("query repo error." + err.Error()) | |||||
| mailer.SendWarnNotifyMail(setting.Warn_Notify_Mails, warnEmailMessage) | |||||
| return | return | ||||
| } | } | ||||
| log.Info("start to query wiki data") | log.Info("start to query wiki data") | ||||
| @@ -56,16 +200,25 @@ func TimingCountDataByDate(date string) { | |||||
| } | } | ||||
| } | } | ||||
| //other user info data | //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") | log.Info("start to time count data") | ||||
| currentTimeNow := time.Now() | currentTimeNow := time.Now() | ||||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | ||||
| startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") | startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") | ||||
| TimingCountDataByDate(startTime) | |||||
| TimingCountDataByDateAndReCount(startTime, false) | |||||
| } | } | ||||
| @@ -12,8 +12,10 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| gotemplate "html/template" | gotemplate "html/template" | ||||
| "io/ioutil" | "io/ioutil" | ||||
| "net/http" | |||||
| "net/url" | "net/url" | ||||
| "path" | "path" | ||||
| "sort" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| @@ -31,11 +33,12 @@ import ( | |||||
| ) | ) | ||||
| const ( | 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 { | type namedBlob struct { | ||||
| @@ -243,6 +246,11 @@ func renderDirectory(ctx *context.Context, treeLink string) { | |||||
| ctx.Data["ReadmeInList"] = true | ctx.Data["ReadmeInList"] = true | ||||
| ctx.Data["ReadmeExist"] = true | ctx.Data["ReadmeExist"] = true | ||||
| ctx.Data["FileIsSymlink"] = readmeFile.isSymlink | ctx.Data["FileIsSymlink"] = readmeFile.isSymlink | ||||
| ctx.Data["ReadmeName"] = readmeFile.name | |||||
| if ctx.Repo.CanEnableEditor() { | |||||
| ctx.Data["CanEditFile"] = true | |||||
| } | |||||
| dataRc, err := readmeFile.blob.DataAsync() | dataRc, err := readmeFile.blob.DataAsync() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -570,19 +578,29 @@ func safeURL(address string) string { | |||||
| } | } | ||||
| type ContributorInfo struct { | 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 { | for _, c := range contributorInfos { | ||||
| if strings.Compare(c.Email,email) == 0 { | |||||
| if strings.Compare(c.Email, email) == 0 { | |||||
| return c | return c | ||||
| } | } | ||||
| } | } | ||||
| return nil | return nil | ||||
| } | } | ||||
| // Home render repository home page | // Home render repository home page | ||||
| func Home(ctx *context.Context) { | func Home(ctx *context.Context) { | ||||
| if len(ctx.Repo.Units) > 0 { | if len(ctx.Repo.Units) > 0 { | ||||
| @@ -591,35 +609,41 @@ func Home(ctx *context.Context) { | |||||
| if err == nil && contributors != nil { | if err == nil && contributors != nil { | ||||
| startTime := time.Now() | startTime := time.Now() | ||||
| var contributorInfos []*ContributorInfo | var contributorInfos []*ContributorInfo | ||||
| contributorInfoHash:= make(map[string]*ContributorInfo) | |||||
| contributorInfoHash := make(map[string]*ContributorInfo) | |||||
| count := 0 | |||||
| for _, c := range contributors { | for _, c := range contributors { | ||||
| if strings.Compare(c.Email,"") == 0 { | |||||
| if count >= 25 { | |||||
| continue | |||||
| } | |||||
| if strings.Compare(c.Email, "") == 0 { | |||||
| continue | continue | ||||
| } | } | ||||
| // get user info from committer email | // get user info from committer email | ||||
| user, err := models.GetUserByActivateEmail(c.Email) | user, err := models.GetUserByActivateEmail(c.Email) | ||||
| if err == nil { | if err == nil { | ||||
| // committer is system user, get info through user's primary email | // 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 | // existed: same primary email, different committer name | ||||
| existedContributorInfo.CommitCnt += c.CommitCnt | existedContributorInfo.CommitCnt += c.CommitCnt | ||||
| }else{ | |||||
| } else { | |||||
| // new committer info | // new committer info | ||||
| var newContributor = &ContributorInfo{ | 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 | contributorInfoHash[user.Email] = newContributor | ||||
| } | } | ||||
| } else { | } else { | ||||
| // committer is not system user | // 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 | // existed: same primary email, different committer name | ||||
| existedContributorInfo.CommitCnt += c.CommitCnt | existedContributorInfo.CommitCnt += c.CommitCnt | ||||
| }else{ | |||||
| } else { | |||||
| var newContributor = &ContributorInfo{ | var newContributor = &ContributorInfo{ | ||||
| user, c.Email,c.CommitCnt, | |||||
| user, "", "",c.Email, c.CommitCnt, | |||||
| } | } | ||||
| count++ | |||||
| contributorInfos = append(contributorInfos, newContributor) | contributorInfos = append(contributorInfos, newContributor) | ||||
| contributorInfoHash[c.Email] = newContributor | contributorInfoHash[c.Email] = newContributor | ||||
| } | } | ||||
| @@ -627,7 +651,7 @@ func Home(ctx *context.Context) { | |||||
| } | } | ||||
| ctx.Data["ContributorInfo"] = contributorInfos | ctx.Data["ContributorInfo"] = contributorInfos | ||||
| var duration = time.Since(startTime) | 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() { | if ctx.Repo.Repository.IsBeingCreated() { | ||||
| task, err := models.GetMigratingTask(ctx.Repo.Repository.ID) | 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) | log.Error("failed to get license content: %v, err:%v", f, err) | ||||
| continue | 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 | ctx.Data["LICENSE"] = f | ||||
| return | 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) { | func renderLanguageStats(ctx *context.Context) { | ||||
| @@ -800,32 +824,29 @@ func renderCode(ctx *context.Context) { | |||||
| */ | */ | ||||
| baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath()) | baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath()) | ||||
| defer baseGitRepo.Close() | defer baseGitRepo.Close() | ||||
| var compareInfo *git.CompareInfo | |||||
| if err != nil { | 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 | 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 | //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 | ctx.Data["UpstreamSameBranchName"] = true | ||||
| }else{ | |||||
| } else { | |||||
| //else, compare between current repo branch and base repo's default branch | //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 | 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() | ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len() | ||||
| }else{ | |||||
| } else { | |||||
| log.Info("compareInfo nothing different") | log.Info("compareInfo nothing different") | ||||
| ctx.Data["FetchUpstreamCnt"] = 0 | ctx.Data["FetchUpstreamCnt"] = 0 | ||||
| } | } | ||||
| }else{ | |||||
| } else { | |||||
| ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error | ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error | ||||
| } | } | ||||
| } | } | ||||
| @@ -893,3 +914,68 @@ func Forks(ctx *context.Context) { | |||||
| ctx.HTML(200, tplForks) | 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 { | m.Head("/", func() string { | ||||
| return "" | return "" | ||||
| }) | }) | ||||
| m.Get("/", routers.Dashboard) | |||||
| m.Get("/", routers.Home) | |||||
| m.Get("/dashboard", routers.Dashboard) | m.Get("/dashboard", routers.Dashboard) | ||||
| m.Group("/explore", func() { | m.Group("/explore", func() { | ||||
| m.Get("", func(ctx *context.Context) { | m.Get("", func(ctx *context.Context) { | ||||
| @@ -325,6 +325,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/organizations", routers.ExploreOrganizations) | m.Get("/organizations", routers.ExploreOrganizations) | ||||
| m.Get("/code", routers.ExploreCode) | m.Get("/code", routers.ExploreCode) | ||||
| m.Get("/images", routers.ExploreImages) | m.Get("/images", routers.ExploreImages) | ||||
| m.Get("/data_analysis", routers.ExploreDataAnalysis) | |||||
| }, ignSignIn) | }, ignSignIn) | ||||
| m.Combo("/install", routers.InstallInit).Get(routers.Install). | m.Combo("/install", routers.InstallInit).Get(routers.Install). | ||||
| Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) | Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) | ||||
| @@ -615,6 +616,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| //reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | //reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | ||||
| // ***** START: Organization ***** | // ***** START: Organization ***** | ||||
| m.Group("/org", func() { | |||||
| m.Group("/:org", func() { | |||||
| m.Get("/members", org.Members) | |||||
| }, context.OrgAssignment()) | |||||
| }) | |||||
| m.Group("/org", func() { | m.Group("/org", func() { | ||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Get("/create", org.Create) | m.Get("/create", org.Create) | ||||
| @@ -625,7 +631,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/dashboard", user.Dashboard) | m.Get("/dashboard", user.Dashboard) | ||||
| m.Get("/^:type(issues|pulls)$", user.Issues) | m.Get("/^:type(issues|pulls)$", user.Issues) | ||||
| m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones) | m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones) | ||||
| m.Get("/members", org.Members) | |||||
| //m.Get("/members", org.Members) | |||||
| m.Post("/members/action/:action", org.MembersAction) | m.Post("/members/action/:action", org.MembersAction) | ||||
| m.Get("/teams", org.Teams) | m.Get("/teams", org.Teams) | ||||
| @@ -786,9 +792,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) | }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) | ||||
| m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) | 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 | // Grouping for those endpoints not requiring authentication | ||||
| m.Group("/:username/:reponame", func() { | m.Group("/:username/:reponame", func() { | ||||
| m.Get("/contributors", repo.Contributors) | |||||
| m.Get("/contributors/list", repo.ContributorsAPI) | |||||
| m.Group("/milestone", func() { | m.Group("/milestone", func() { | ||||
| m.Get("/:id", repo.MilestoneIssuesAndPulls) | m.Get("/:id", repo.MilestoneIssuesAndPulls) | ||||
| }, reqRepoIssuesOrPullsReader, context.RepoRef()) | }, reqRepoIssuesOrPullsReader, context.RepoRef()) | ||||
| @@ -962,16 +970,6 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| }, context.RepoRef()) | }, context.RepoRef()) | ||||
| m.Group("/modelarts", func() { | 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.Group("/notebook", func() { | ||||
| m.Get("", reqRepoCloudBrainReader, repo.NotebookIndex) | m.Get("", reqRepoCloudBrainReader, repo.NotebookIndex) | ||||
| m.Group("/:jobid", func() { | m.Group("/:jobid", func() { | ||||
| @@ -990,12 +988,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow) | m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow) | ||||
| m.Post("/stop", reqRepoCloudBrainWriter, repo.TrainJobStop) | m.Post("/stop", reqRepoCloudBrainWriter, repo.TrainJobStop) | ||||
| m.Post("/del", reqRepoCloudBrainWriter, repo.TrainJobDel) | 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.Get("/create", reqRepoCloudBrainReader, repo.TrainJobNew) | ||||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate) | m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate) | ||||
| m.Get("/para-config-list", reqRepoCloudBrainReader, repo.TrainJobGetConfigList) | m.Get("/para-config-list", reqRepoCloudBrainReader, repo.TrainJobGetConfigList) | ||||
| }) | }) | ||||
| }, context.RepoRef()) | }, 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 { | if err := models.UpdateUserCols(u, "language"); err != nil { | ||||
| log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language)) | log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language)) | ||||
| return setting.AppSubURL + "/" | |||||
| return setting.AppSubURL + "/dashboard" | |||||
| } | } | ||||
| } else { | } else { | ||||
| // Language setting of the user use the one previously set | // 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() | u.SetLastLogin() | ||||
| if err := models.UpdateUserCols(u, "last_login_unix"); err != nil { | if err := models.UpdateUserCols(u, "last_login_unix"); err != nil { | ||||
| ctx.ServerError("UpdateUserCols", err) | ctx.ServerError("UpdateUserCols", err) | ||||
| return setting.AppSubURL + "/" | |||||
| return setting.AppSubURL + "/dashboard" | |||||
| } | } | ||||
| if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) { | 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 { | if obeyRedirect { | ||||
| ctx.Redirect(setting.AppSubURL + "/") | |||||
| ctx.Redirect(setting.AppSubURL + "/dashboard") | |||||
| } | } | ||||
| return setting.AppSubURL + "/" | |||||
| return setting.AppSubURL + "/dashboard" | |||||
| } | } | ||||
| // SignInOAuth handles the OAuth2 login buttons | // SignInOAuth handles the OAuth2 login buttons | ||||
| @@ -115,6 +115,22 @@ func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAdd | |||||
| SendAsync(msg) | 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. | // SendRegisterNotifyMail triggers a notify e-mail by admin created a account. | ||||
| func SendRegisterNotifyMail(locale Locale, u *models.User) { | func SendRegisterNotifyMail(locale Locale, u *models.User) { | ||||
| if setting.MailService == nil { | 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 || []; | var _hmt = _hmt || []; | ||||
| (function() { | (function() { | ||||
| var hm = document.createElement("script"); | 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); | s.parentNode.insertBefore(hm, s); | ||||
| })(); | })(); | ||||
| </script> | </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 || []; | var _hmt = _hmt || []; | ||||
| (function() { | (function() { | ||||
| var hm = document.createElement("script"); | 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); | s.parentNode.insertBefore(hm, s); | ||||
| })(); | })(); | ||||
| </script> | </script> | ||||
| @@ -7,6 +7,14 @@ | |||||
| <i class="sidebar icon"></i> | <i class="sidebar icon"></i> | ||||
| </div> | </div> | ||||
| </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}} | {{if .IsSigned}} | ||||
| <a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a> | <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/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</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> | <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> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageHome}} | {{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/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</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> | <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> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageExplore}} | {{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/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</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> | <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> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageHome}} | {{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/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</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> | <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> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageExplore}} | {{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> | </div> | ||||
| </h1> | </h1> | ||||
| <p class="am-lh-18">免费私有代码仓库,免费计算资源,大容量数据存储,<br>多类型硬件环境(GPU、NPU),AI开发流水线(开发-调试-训练-迭代)</p> | <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 class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div> | ||||
| </div> | </div> | ||||
| </div><!-- end segment --> | </div><!-- end segment --> | ||||
| @@ -33,7 +37,11 @@ | |||||
| <p class="am-lh-18">在这里为你和你的团队创建项目,基于Git工具,提交记录或者回滚代码修改。<br> | <p class="am-lh-18">在这里为你和你的团队创建项目,基于Git工具,提交记录或者回滚代码修改。<br> | ||||
| 不论是公开或者私有仓库,都可免费使用所有功能。<br> | 不论是公开或者私有仓库,都可免费使用所有功能。<br> | ||||
| 尽情将你喜欢的代码都放在这里,仓库数量、存储容量不受限</p> | 尽情将你喜欢的代码都放在这里,仓库数量、存储容量不受限</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> | ||||
| <div class="ten wide column computer only i-code-pic am-pt-30"> | <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;"> | <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> | ||||
| 如果您的模型需要更多的计算资源,也可以单独申请<br> | 如果您的模型需要更多的计算资源,也可以单独申请<br> | ||||
| </p> | </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> | </div> | ||||
| </div> | </div> | ||||
| @@ -38,11 +38,12 @@ | |||||
| <div class="ui sixteen wide mobile six wide tablet five wide computer column"> | <div class="ui sixteen wide mobile six wide tablet five wide computer column"> | ||||
| <h4 class="ui top attached header"> | <h4 class="ui top attached header"> | ||||
| <strong>{{.i18n.Tr "org.people"}}</strong> | <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> | </h4> | ||||
| <div class="ui attached segment members"> | <div class="ui attached segment members"> | ||||
| {{$isMember := .IsOrganizationMember}} | {{$isMember := .IsOrganizationMember}} | ||||
| @@ -3,10 +3,10 @@ | |||||
| <a class="{{if $.PageIsOrgHome}}active{{end}} item" href="{{.HomeLink}}"> | <a class="{{if $.PageIsOrgHome}}active{{end}} item" href="{{.HomeLink}}"> | ||||
| {{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | {{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | ||||
| </a> | </a> | ||||
| <a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||||
| {{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||||
| </a> | |||||
| {{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}} | {{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"> | <a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams"> | ||||
| {{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | {{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | ||||
| </a> | </a> | ||||
| @@ -23,10 +23,10 @@ | |||||
| {{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | {{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| <a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||||
| {{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||||
| </a> | |||||
| {{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}} | {{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"> | <a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams"> | ||||
| {{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | {{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | ||||
| </a> | </a> | ||||
| @@ -2,8 +2,18 @@ | |||||
| <div class="repository commits"> | <div class="repository commits"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <div class="ui container"> | <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 --> | <!-- Period --> | ||||
| <div class="ui floating dropdown jump filter"> | <div class="ui floating dropdown jump filter"> | ||||
| <div class="ui basic compact button"> | <div class="ui basic compact button"> | ||||
| @@ -23,7 +33,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </h2> | |||||
| </div> | |||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| {{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}} | {{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}} | ||||
| @@ -239,8 +239,8 @@ | |||||
| <div class="column"> | <div class="column"> | ||||
| <div class="ui blue small menu compact selectcloudbrain"> | <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> | </div> | ||||
| <div class="column right aligned"> | <div class="column right aligned"> | ||||
| @@ -307,9 +307,9 @@ | |||||
| <!-- 任务名 --> | <!-- 任务名 --> | ||||
| <div class="five wide column"> | <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> | </a> | ||||
| </div> | </div> | ||||
| @@ -380,7 +380,7 @@ | |||||
| {{end}} | {{end}} | ||||
| </form> | </form> | ||||
| </div> | </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"> | <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | ||||
| {{$.i18n.Tr "repo.download"}} | {{$.i18n.Tr "repo.download"}} | ||||
| @@ -399,11 +399,11 @@ | |||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | ||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | {{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"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{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"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| @@ -427,12 +427,12 @@ | |||||
| <div class="inline required field dis"> | <div class="inline required field dis"> | ||||
| <label>镜像标签:</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <label class="label_after">镜像描述:</label> | <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> | ||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| @@ -131,7 +131,7 @@ | |||||
| <div class="ui attached segment"> | <div class="ui attached segment"> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>任务名称</label> | <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> | ||||
| <div class="inline required field" style="{{if ((.is_benchmark_enabled) or (.is_snn4imagenet_enabled) or (.is_brainscore_enabled))}}display:block;{{else}}display:none;{{end}}"> | <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> | </div> | ||||
| <input id="store_category" type="hidden" name="get_benchmark_category"> | <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> | <label>GPU类型</label> | ||||
| <select id="cloudbrain_gpu_type" class="ui search dropdown" placeholder="选择GPU类型" style='width:385px' name="gpu_type"> | <select id="cloudbrain_gpu_type" class="ui search dropdown" placeholder="选择GPU类型" style='width:385px' name="gpu_type"> | ||||
| {{range .gpu_types}} | {{range .gpu_types}} | ||||
| @@ -176,7 +176,7 @@ | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>镜像</label> | <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"> | <datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image"> | ||||
| {{range .images}} | {{range .images}} | ||||
| <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | ||||
| @@ -208,27 +208,27 @@ | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>数据集存放路径</label> | <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> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>模型存放路径</label> | <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> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>代码存放路径</label> | <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> | ||||
| <div class="inline required field cloudbrain_benchmark"> | <div class="inline required field cloudbrain_benchmark"> | ||||
| <label>benchmark脚本存放路径</label> | <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> | ||||
| <div class="inline required field cloudbrain_snn4imagenet"> | <div class="inline required field cloudbrain_snn4imagenet"> | ||||
| <label>snn4imagenet脚本存放路径</label> | <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> | ||||
| <div class="inline required field cloudbrain_brainscore"> | <div class="inline required field cloudbrain_brainscore"> | ||||
| <label>brainscore脚本存放路径</label> | <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> | ||||
| <div class="inline required field" hidden> | <div class="inline required field" hidden> | ||||
| <label>启动命令</label> | <label>启动命令</label> | ||||
| @@ -6,7 +6,19 @@ | |||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| <h4 class="ui header" id="vertical-segment"> | <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> | </h4> | ||||
| <div> | <div> | ||||
| <div class="ui yellow segment"> | <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> | ||||
| <div class="inline field {{if .Err_Description}}error{{end}}"> | <div class="inline field {{if .Err_Description}}error{{end}}"> | ||||
| <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <label>{{.i18n.Tr "repo.template"}}</label> | <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"> | <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> | <label class="d-block">{{.i18n.Tr "dataset.title"}}</label> | ||||
| <div class="sixteen wide column"> | <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> | </div> | ||||
| <label class="d-block">{{.i18n.Tr "dataset.description"}}</label> | <label class="d-block">{{.i18n.Tr "dataset.description"}}</label> | ||||
| <div class="sixteen wide column"> | <div class="sixteen wide column"> | ||||
| @@ -92,16 +92,27 @@ | |||||
| {{if not .Repository.IsBeingCreated}} | {{if not .Repository.IsBeingCreated}} | ||||
| <div class="ui tabular stackable menu navbar"> | <div class="ui tabular stackable menu navbar"> | ||||
| {{if .Permission.CanRead $.UnitTypeCode}} | {{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> | </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}} | {{end}} | ||||
| {{if .Permission.CanRead $.UnitTypeIssues}} | {{if .Permission.CanRead $.UnitTypeIssues}} | ||||
| <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues"> | ||||
| @@ -109,11 +120,11 @@ | |||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{if .Permission.CanRead $.UnitTypeExternalTracker}} | |||||
| <!-- {{if .Permission.CanRead $.UnitTypeExternalTracker}} | |||||
| <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer"> | <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer"> | ||||
| {{svg "octicon-link-external" 16}} {{.i18n.Tr "repo.issues"}} </span> | {{svg "octicon-link-external" 16}} {{.i18n.Tr "repo.issues"}} </span> | ||||
| </a> | </a> | ||||
| {{end}} | |||||
| {{end}} --> | |||||
| {{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}} | {{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}} | ||||
| <a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | <a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | ||||
| @@ -121,35 +132,22 @@ | |||||
| </a> | </a> | ||||
| {{end}} | {{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> | </a> | ||||
| {{end}} | {{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}} | {{if .Permission.CanRead $.UnitTypeCloudBrain}} | ||||
| <a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/cloudbrain"> | <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> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{if .IsSigned}} | |||||
| <!-- {{if .IsSigned}} | |||||
| <a class="{{if .PageIsBlockChain}}active{{end}} item " href="{{.RepoLink}}/blockchain"> | <a class="{{if .PageIsBlockChain}}active{{end}} item " href="{{.RepoLink}}/blockchain"> | ||||
| {{svg "octicon-law" 16}} | {{svg "octicon-law" 16}} | ||||
| {{.i18n.Tr "repo.balance"}} | {{.i18n.Tr "repo.balance"}} | ||||
| </a> | </a> | ||||
| {{end}} | |||||
| {{end}} --> | |||||
| {{template "custom/extra_tabs" .}} | {{template "custom/extra_tabs" .}} | ||||
| @@ -243,4 +241,9 @@ | |||||
| window.location.href = repolink + "/datasets?type=" + checked_radio | 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> | </script> | ||||
| @@ -4,7 +4,7 @@ | |||||
| font-size: 1.0em; | font-size: 1.0em; | ||||
| margin-bottom: 1.0rem; | margin-bottom: 1.0rem; | ||||
| } | } | ||||
| #contributorInfo > a:nth-child(n+25){ | |||||
| #contributorInfo > a:nth-child(n+26){ | |||||
| display:none; | display:none; | ||||
| } | } | ||||
| #contributorInfo > a{ | #contributorInfo > a{ | ||||
| @@ -329,9 +329,15 @@ | |||||
| <div> | <div> | ||||
| <h4 class="ui header"> | <h4 class="ui header"> | ||||
| {{$lenCon := len .ContributorInfo}} | |||||
| {{if lt $lenCon 25 }} | |||||
| <strong>贡献者 ({{len .ContributorInfo}})</strong> | <strong>贡献者 ({{len .ContributorInfo}})</strong> | ||||
| {{else}} | |||||
| <strong>贡献者 ({{len .ContributorInfo}}+)</strong> | |||||
| {{end}} | |||||
| <div class="ui right"> | <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> | </div> | ||||
| </h4> | </h4> | ||||
| <div class="ui members" id="contributorInfo"> | <div class="ui members" id="contributorInfo"> | ||||
| @@ -353,10 +359,10 @@ | |||||
| </div> | </div> | ||||
| <script type="text/javascript"> | <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> | </script> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -4,7 +4,7 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.Title | RenderEmoji}}</div> | <div class="action section">{{.Title | RenderEmoji}}</div> | ||||
| @@ -4,7 +4,7 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <div class="ui three column stackable grid"> | <div class="ui three column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> | <a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> | ||||
| @@ -4,7 +4,7 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> | <a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a> | ||||
| @@ -4,7 +4,7 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.Title | RenderEmoji}}</div> | <div class="action section">{{.Title | RenderEmoji}}</div> | ||||
| @@ -4,7 +4,7 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.i18n.Tr "repo.issues.new"}}</div> | <div class="action section">{{.i18n.Tr "repo.issues.new"}}</div> | ||||
| @@ -15,7 +15,7 @@ | |||||
| <div class="ui segment content"> | <div class="ui segment content"> | ||||
| <div class="field"> | <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}} | {{if .PageIsComparePull}} | ||||
| <div class="title_wip_desc">{{.i18n.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> | <div class="title_wip_desc">{{.i18n.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> | ||||
| {{end}} | {{end}} | ||||
| @@ -5,13 +5,13 @@ | |||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <div class="column" style="display: flex;align-items: center;"> | ||||
| {{if .PageIsIssueList}} | {{if .PageIsIssueList}} | ||||
| <div class="ui large breadcrumb"> | |||||
| <div class="ui breadcrumb"> | |||||
| <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | <a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | ||||
| </div> | </div> | ||||
| {{else}} | {{else}} | ||||
| <div class="ui large breadcrumb"> | |||||
| <div class="ui breadcrumb"> | |||||
| <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | ||||
| @@ -3,7 +3,7 @@ | |||||
| <h1 class="twelve wide column"> | <h1 class="twelve wide column"> | ||||
| <span class="index">#{{.Issue.Index}}</span> <span id="issue-title">{{RenderEmoji .Issue.Title}}</span> | <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"> | <div id="edit-title-input" class="ui input" style="display: none"> | ||||
| <input value="{{.Issue.Title}}" maxlength="255"> | |||||
| <input value="{{.Issue.Title}}" maxlength="254"> | |||||
| </div> | </div> | ||||
| </h1> | </h1> | ||||
| {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} | {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} | ||||
| @@ -122,7 +122,7 @@ | |||||
| </div> | </div> | ||||
| <div class="inline field {{if .Err_Description}}error{{end}}"> | <div class="inline field {{if .Err_Description}}error{{end}}"> | ||||
| <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> | <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> | ||||
| <div class="inline field"> | <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"> | <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> | </a> | ||||
| </div> | </div> | ||||
| @@ -352,11 +352,11 @@ | |||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | ||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | {{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"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{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"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| @@ -112,7 +112,7 @@ | |||||
| <!-- <br> --> | <!-- <br> --> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>任务名称</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| @@ -128,28 +128,28 @@ | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>工作环境</label> | <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> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>类型</label> | <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> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>规格</label> | <label>规格</label> | ||||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | ||||
| {{range .flavors}} | {{range .flavors}} | ||||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||||
| <option name="flavor" value="{{.Value}}">{{.Desc}}</option> | |||||
| {{end}} | {{end}} | ||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>数据集存放路径</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <label>描述</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <label></label> | <label></label> | ||||
| @@ -6,7 +6,19 @@ | |||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| <h4 class="ui header" id="vertical-segment"> | <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> | </h4> | ||||
| <div> | <div> | ||||
| <div class="ui yellow segment"> | <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> | <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}</h4> | ||||
| <div class="required field"> | <div class="required field"> | ||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | <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> | ||||
| <div class="field"> | <div class="field"> | ||||
| <label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}</label> | <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> | </div> | ||||
| <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}</h4> | <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}</h4> | ||||
| <div class="required field"> | <div class="required field"> | ||||
| @@ -52,7 +52,7 @@ | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | <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> | <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> | <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> | </span> | ||||
| @@ -128,7 +128,7 @@ | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <button class="ui green button"> | <button class="ui green button"> | ||||
| @@ -180,6 +180,12 @@ | |||||
| cursor: pointer; | cursor: pointer; | ||||
| pointer-events: none; | pointer-events: none; | ||||
| } | } | ||||
| .fontsize14{ | |||||
| font-size: 14px; | |||||
| } | |||||
| .padding0{ | |||||
| padding: 0 !important; | |||||
| } | |||||
| </style> | </style> | ||||
| <!-- 弹窗 --> | <!-- 弹窗 --> | ||||
| @@ -232,13 +238,13 @@ | |||||
| <div class="column"> | <div class="column"> | ||||
| <div class="ui blue small menu compact selectcloudbrain"> | <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> | <a class="active item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="column right aligned"> | <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}} | {{svg "octicon-server" 16}} | ||||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | ||||
| <i class="dropdown icon"></i> | <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}}/cloudbrain" data-value="11">CPU / GPU</a> | ||||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="22">Ascend NPU</a> | <a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="22">Ascend NPU</a> | ||||
| </div> | </div> | ||||
| </div> | |||||
| </div> --> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | {{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}} | <a class="ui green button" href="{{.RepoLink}}/modelarts/train-job/create">{{$.i18n.Tr "repo.modelarts.train_job.new_train"}}</a>{{end}} | ||||
| </div> | </div> | ||||
| @@ -278,20 +284,29 @@ | |||||
| <!-- 表头 --> | <!-- 表头 --> | ||||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | <div class="ui grid stackable" style="background: #f0f0f0;;"> | ||||
| <div class="row"> | <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> | <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | ||||
| </div> | </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> | ||||
| <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> | <span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span> | ||||
| </div> | </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> | <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | ||||
| </div> | </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> | ||||
| </div> | </div> | ||||
| @@ -305,38 +320,44 @@ | |||||
| <div class="row"> | <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> | </a> | ||||
| </div> | </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 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><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> | ||||
| <!-- 任务创建时间 --> | |||||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</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> | ||||
| <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> | ||||
| <div class="one wide column text center"> | |||||
| <!-- 创建者 --> | |||||
| <div class="one wide column text center padding0"> | |||||
| {{if .User.Name}} | {{if .User.Name}} | ||||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | ||||
| {{else}} | {{else}} | ||||
| @@ -344,56 +365,55 @@ | |||||
| {{end}} | {{end}} | ||||
| </div> | </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}} | {{$.CsrfTokenHtml}} | ||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | {{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"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{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"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| </form> | </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"}} | {{$.i18n.Tr "repo.model_download"}} | ||||
| </a> | </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"> | <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | ||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | {{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"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{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"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} {{template "base/paginate" .}} | {{end}} {{template "base/paginate" .}} | ||||
| @@ -435,6 +455,8 @@ | |||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| <script> | <script> | ||||
| console.log({{.Tasks}}) | |||||
| // 调试和评分新开窗口 | // 调试和评分新开窗口 | ||||
| function stop(obj) { | function stop(obj) { | ||||
| if (obj.style.color != "rgb(204, 204, 204)") { | if (obj.style.color != "rgb(204, 204, 204)") { | ||||
| @@ -484,11 +506,12 @@ | |||||
| $(".job-status").each((index, job) => { | $(".job-status").each((index, job) => { | ||||
| const jobID = job.dataset.jobid; | const jobID = job.dataset.jobid; | ||||
| const repoPath = job.dataset.repopath; | 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 duration = data.JobDuration | ||||
| const jobID = data.JobID | 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) => { | $(".job-status").each((index, job) => { | ||||
| const jobID = job.dataset.jobid; | const jobID = job.dataset.jobid; | ||||
| const repoPath = job.dataset.repopath; | 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' | 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() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED' | ||||
| || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') { | || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') { | ||||
| return | 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 jobID = data.JobID | ||||
| const status = data.JobStatus | const status = data.JobStatus | ||||
| const duration = data.JobDuration | const duration = data.JobDuration | ||||
| $('#duration-'+jobID).text(duration) | |||||
| if (status != job.textContent.trim()) { | if (status != job.textContent.trim()) { | ||||
| $('#' + jobID+'-icon').removeClass().addClass(status) | $('#' + jobID+'-icon').removeClass().addClass(status) | ||||
| $('#' + jobID+ '-text').text(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) { | }).fail(function(err) { | ||||
| console.log(err); | console.log(err); | ||||
| }); | }); | ||||
| @@ -580,7 +577,36 @@ | |||||
| modal.style.display = "none"; | 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() { | function showmask() { | ||||
| $('#imageModal').css('display', 'none') | $('#imageModal').css('display', 'none') | ||||
| @@ -103,7 +103,9 @@ | |||||
| -webkit-animation-delay: -0.8s; | -webkit-animation-delay: -0.8s; | ||||
| animation-delay: -0.8s; | animation-delay: -0.8s; | ||||
| } | } | ||||
| .left2{ | |||||
| margin-left: -2px; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | @-webkit-keyframes sk-stretchdelay { | ||||
| 0%, | 0%, | ||||
| 40%, | 40%, | ||||
| @@ -153,95 +155,81 @@ | |||||
| <form class="ui form" action="{{.Link}}" method="post"> | <form class="ui form" action="{{.Link}}" method="post"> | ||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| <input type="hidden" name="action" value="update"> | <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> | <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | ||||
| <div class="required unite min_title inline field"> | <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> | ||||
| <div class="unite min_title inline field"> | <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> | </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> | <div class="ui divider"></div> | ||||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4> | <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%;"> | <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;"> | <div class="field" style="flex: 1.5;"> | ||||
| <select class="ui dropdown width" id="trainjob_engines" > | <select class="ui dropdown width" id="trainjob_engines" > | ||||
| {{range .engines}} | {{range .engines}} | ||||
| <option value="{{.Value}}">{{.Value}}</option> | <option value="{{.Value}}">{{.Value}}</option> | ||||
| {{end}} | {{end}} | ||||
| </select> | </select> | ||||
| </div> | </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"> | <select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;' name="engine_id"> | ||||
| {{range .engine_versions}} | {{range .engine_versions}} | ||||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | ||||
| {{end}} | {{end}} | ||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| </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"> | <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}} | {{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}} | {{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}} | {{end}} | ||||
| <span> | <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> | <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> | </span> | ||||
| </div> | </div> | ||||
| <div class="required unite min_title inline field"> | <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="选择数据集"> | <select class="ui dropdown width80" id="trainjob_datasets" name="attachment" placeholder="选择数据集"> | ||||
| {{if $.uuid}} | {{if $.uuid}} | ||||
| <option name="attachment" value="{{$.uuid}}">{{$.datasetName}}</option> | <option name="attachment" value="{{$.uuid}}">{{$.datasetName}}</option> | ||||
| @@ -254,19 +242,32 @@ | |||||
| </div> | </div> | ||||
| <div class="inline unite min_title field"> | <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> | <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"> | <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> | </div> | ||||
| <!-- <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.resource_setting"}}</h4> --> | |||||
| <div class="required field " style="display: none;"> | <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"> | <select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id"> | ||||
| {{range .resource_pools}} | {{range .resource_pools}} | ||||
| <option value="{{.ID}}">{{.Value}}</option> | <option value="{{.ID}}">{{.Value}}</option> | ||||
| @@ -275,7 +276,7 @@ | |||||
| </div> | </div> | ||||
| <div class="required grouped fields" style="display: none;"> | <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="field"> | ||||
| <div class="ui grid"> | <div class="ui grid"> | ||||
| <div class="column"> | <div class="column"> | ||||
| @@ -290,8 +291,8 @@ | |||||
| </div> | </div> | ||||
| </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"> | <select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor"> | ||||
| {{range .flavor_infos}} | {{range .flavor_infos}} | ||||
| <option name="flavor" value="{{.Code}}">{{.Value}}</option> | <option name="flavor" value="{{.Code}}">{{.Value}}</option> | ||||
| @@ -299,39 +300,15 @@ | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <div class="inline required unite min_title field"> | <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%;"> | <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> | </div> | ||||
| --> | |||||
| <div class="inline unite min_title field"> | <div class="inline unite min_title field"> | ||||
| <button class="ui create_train_job green button"> | <button class="ui create_train_job green button"> | ||||
| @@ -598,8 +575,15 @@ | |||||
| msg = JSON.stringify(msg) | msg = JSON.stringify(msg) | ||||
| $('#store_run_para').val(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) { | $('.ui.create_train_job.green.button').click(function(e) { | ||||
| get_name() | |||||
| send_run_para() | send_run_para() | ||||
| validate() | validate() | ||||
| }) | }) | ||||
| @@ -38,7 +38,7 @@ | |||||
| <!-- 任务名 --> | <!-- 任务名 --> | ||||
| <div class="five wide column"> | <div class="five wide column"> | ||||
| <a class="title" href="{{$.Link}}/{{.JobID}}"> | <a class="title" href="{{$.Link}}/{{.JobID}}"> | ||||
| <span class="fitted">{{svg "octicon-tasklist" 16}}</span> | |||||
| <span class="fitted">{{.JobName}}</span> | <span class="fitted">{{.JobName}}</span> | ||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| @@ -1,139 +1,470 @@ | |||||
| {{template "base/head" .}} | {{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"> | <div class="repository"> | ||||
| {{template "repo/header" .}} | {{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> | ||||
| <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> | ||||
| </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> | ||||
| <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> | ||||
| <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> | </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> | ||||
| <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> | ||||
| <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> | </tr> | ||||
| </tbody> | </tbody> | ||||
| </table> | </table> | ||||
| </div> | </div> | ||||
| </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> --> | |||||
| <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> | |||||
| </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> | </div> | ||||
| </div> | </div> | ||||
| @@ -142,59 +473,285 @@ | |||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| <script> | <script> | ||||
| console.log({{.version_list_task}}) | |||||
| $('.menu .item').tab() | $('.menu .item').tab() | ||||
| $('.ui.style.accordion').accordion(); | |||||
| // $('.ui.style.accordion').accordion(); | |||||
| var userName | |||||
| var repoPath | |||||
| var jobID | |||||
| $(document).ready(function(){ | $(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] | userName = urlArr.slice(-5)[0] | ||||
| repoPath = urlArr.slice(-4)[0] | repoPath = urlArr.slice(-4)[0] | ||||
| jobID = urlArr.slice(-1)[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(){ | setTimeout(function(){ | ||||
| $('.message').css('display', 'none') | |||||
| $(`.message${version_name}`).css('display', 'none') | |||||
| }, 1000) | }, 1000) | ||||
| }else{ | }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) { | }).fail(function(err) { | ||||
| console.log(err); | console.log(err); | ||||
| }); | }); | ||||
| } | } | ||||
| if(scrollTop == 0){ | 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(){ | setTimeout(function(){ | ||||
| $('.message').css('display', 'none') | |||||
| $(`.message${version_name}`).css('display', 'none') | |||||
| }, 1000) | }, 1000) | ||||
| }else{ | }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) { | }).fail(function(err) { | ||||
| console.log(err); | console.log(err); | ||||
| }); | }); | ||||
| } | } | ||||
| }) | |||||
| } | |||||
| </script> | </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> --> | ||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | ||||
| @@ -10,7 +10,7 @@ | |||||
| </div> --> | </div> --> | ||||
| <div class="ui two column stackable grid"> | <div class="ui two column stackable grid"> | ||||
| <div class="column" style="display: flex;align-items: center;"> | <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> | <a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a> | ||||
| <div class="divider"> / </div> | <div class="divider"> / </div> | ||||
| <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | <div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div> | ||||
| @@ -53,7 +53,7 @@ | |||||
| </div> | </div> | ||||
| <div class="inline field {{if .Err_Description}}error{{end}}"> | <div class="inline field {{if .Err_Description}}error{{end}}"> | ||||
| <label for="description">{{.i18n.Tr "repo.repo_desc"}}</label> | <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> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| @@ -4,7 +4,11 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| <h2 class="ui header"> | <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}} | {{if .CanCreateRelease}} | ||||
| <div class="ui right"> | <div class="ui right"> | ||||
| <a class="ui small green button" href="{{$.RepoLink}}/releases/new"> | <a class="ui small green button" href="{{$.RepoLink}}/releases/new"> | ||||
| @@ -19,7 +19,7 @@ | |||||
| {{if .PageIsEditRelease}} | {{if .PageIsEditRelease}} | ||||
| <b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong> | <b>{{.tag_name}}</b><span class="at">@</span><strong>{{.tag_target}}</strong> | ||||
| {{else}} | {{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> | <span class="at">@</span> | ||||
| <div class="ui selection dropdown"> | <div class="ui selection dropdown"> | ||||
| <input type="hidden" name="tag_target" value="{{.tag_target}}"/> | <input type="hidden" name="tag_target" value="{{.tag_target}}"/> | ||||
| @@ -42,7 +42,7 @@ | |||||
| <div class="eleven wide column"> | <div class="eleven wide column"> | ||||
| <div class="field {{if .Err_Title}}error{{end}}"> | <div class="field {{if .Err_Title}}error{{end}}"> | ||||
| <label>{{.i18n.Tr "repo.release.title"}}</label> | <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> | ||||
| <div class="field"> | <div class="field"> | ||||
| <label>{{.i18n.Tr "repo.release.content"}}</label> | <label>{{.i18n.Tr "repo.release.content"}}</label> | ||||
| @@ -41,7 +41,7 @@ | |||||
| {{end}} | {{end}} | ||||
| <div class="field {{if .Err_Description}}error{{end}}"> | <div class="field {{if .Err_Description}}error{{end}}"> | ||||
| <label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label> | <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> | ||||
| <div class="field {{if .Err_Website}}error{{end}}"> | <div class="field {{if .Err_Website}}error{{end}}"> | ||||
| <label for="website">{{.i18n.Tr "repo.settings.site"}}</label> | <label for="website">{{.i18n.Tr "repo.settings.site"}}</label> | ||||
| @@ -34,6 +34,21 @@ | |||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| </div> | </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}} | {{if not .ReadmeInList}} | ||||
| <div class="file-header-right"> | <div class="file-header-right"> | ||||
| <div class="ui right file-actions"> | <div class="ui right file-actions"> | ||||
| @@ -2,6 +2,13 @@ | |||||
| <div class="repository wiki start"> | <div class="repository wiki start"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <div class="ui container"> | <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"> | <div class="ui center segment"> | ||||
| {{svg "octicon-book" 32}} | {{svg "octicon-book" 32}} | ||||
| <h2>{{.i18n.Tr "repo.wiki.welcome"}}</h2> | <h2>{{.i18n.Tr "repo.wiki.welcome"}}</h2> | ||||