| @@ -5,6 +5,12 @@ type BaseMessage struct { | |||
| Message string | |||
| } | |||
| var BaseMessageOK = BaseMessage{ | |||
| var BaseOKMessage = BaseMessage{ | |||
| 0, "", | |||
| } | |||
| func BaseErrorMessage(message string) BaseMessage { | |||
| return BaseMessage{ | |||
| 1, message, | |||
| } | |||
| } | |||
| @@ -22,7 +22,7 @@ type Dataset struct { | |||
| Category string | |||
| Description string `xorm:"TEXT"` | |||
| DownloadTimes int64 | |||
| NumStars int `xorm:"NOT NULL DEFAULT 0"` | |||
| NumStars int `xorm:"INDEX NOT NULL DEFAULT 0"` | |||
| License string | |||
| Task string | |||
| ReleaseID int64 `xorm:"INDEX"` | |||
| @@ -105,22 +105,6 @@ func CreateDataset(dataset *Dataset) (err error) { | |||
| return nil | |||
| } | |||
| func CreateDefaultDatasetToRepo(repo *Repository) (err error) { | |||
| dataset := &Dataset{RepoID: repo.ID} | |||
| has, err := x.Get(dataset) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if !has { | |||
| dataset.Status = DatasetStatusPrivate | |||
| dataset.Title = repo.Name | |||
| if err = CreateDataset(dataset); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) { | |||
| cond := SearchDatasetCondition(opts) | |||
| return SearchDatasetByCondition(opts, cond) | |||
| @@ -140,6 +124,7 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||
| if opts.IncludePublic { | |||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | |||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||
| if opts.OwnerID > 0 { | |||
| if len(opts.Keyword) == 0 { | |||
| cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) | |||
| @@ -154,6 +139,7 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||
| cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) | |||
| if !opts.IsOwner { | |||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | |||
| cond = cond.And(builder.Eq{"attachment.is_private": false}) | |||
| } | |||
| } | |||
| @@ -170,14 +156,19 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da | |||
| defer sess.Close() | |||
| datasets := make(DatasetList, 0, opts.PageSize) | |||
| selectColumnsSql := "distinct dataset.id,dataset.title, dataset.status, dataset.category, dataset.description, dataset.download_times, dataset.license, dataset.task, dataset.release_id, dataset.user_id, dataset.repo_id, dataset.created_unix,dataset.updated_unix,dataset.num_stars" | |||
| count, err := sess.Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).Count(new(Dataset)) | |||
| count, err := sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). | |||
| Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). | |||
| Where(cond).Count(new(Dataset)) | |||
| if err != nil { | |||
| return nil, 0, fmt.Errorf("Count: %v", err) | |||
| } | |||
| sess.Select("dataset.*").Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||
| sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). | |||
| Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). | |||
| Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||
| if opts.PageSize > 0 { | |||
| sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | |||
| } | |||
| @@ -9,11 +9,10 @@ import ( | |||
| type CreateDatasetForm struct { | |||
| Title string `binding:"Required"` | |||
| Category string `binding:"Required"` | |||
| Description string `binding:"Required;MaxSize(254)"` | |||
| Description string `binding:"Required"` | |||
| License string `binding:"Required;MaxSize(64)"` | |||
| Task string `binding:"Required;MaxSize(64)"` | |||
| ReleaseID int64 `xorm:"INDEX"` | |||
| Private bool | |||
| Files []string | |||
| } | |||
| @@ -25,11 +24,10 @@ type EditDatasetForm struct { | |||
| ID int64 `binding:"Required"` | |||
| Title string `binding:"Required"` | |||
| Category string `binding:"Required"` | |||
| Description string `binding:"Required;MaxSize(254)"` | |||
| Description string `binding:"Required"` | |||
| License string `binding:"Required;MaxSize(64)"` | |||
| Task string `binding:"Required;MaxSize(64)"` | |||
| Private bool | |||
| ReleaseID int64 `xorm:"INDEX"` | |||
| ReleaseID int64 `xorm:"INDEX"` | |||
| Files []string | |||
| Type string `binding:"Required"` | |||
| Type string `binding:"Required"` | |||
| } | |||
| @@ -709,8 +709,12 @@ alert = To initiate a cloud brain task, please upload the dataset in zip format. | |||
| dataset = Dataset | |||
| dataset_setting= Dataset Setting | |||
| title = Name | |||
| title_format_err=Name can only contain number,letter,'-','_' or '.', and can be up to 100 characters long. | |||
| description = Description | |||
| description_format_err=Description's length can be up to 1024 characters long. | |||
| create_dataset = Create Dataset | |||
| create_dataset_fail=Failed to create dataset. | |||
| query_dataset_fail=Failed to query dataset. | |||
| show_dataset= Dataset | |||
| edit_dataset= Edit Dataset | |||
| update_dataset= Update Dataset | |||
| @@ -712,8 +712,13 @@ alert=如果要发起云脑任务,请上传zip格式的数据集 | |||
| dataset=数据集 | |||
| dataset_setting=数据集设置 | |||
| title=名称 | |||
| title_format_err=名称最多允许输入100个字符,只允许字母,数字,中划线 (‘-’),下划线 (‘_’) 和点 (‘.’) 。 | |||
| description=描述 | |||
| description_format_err=描述最多允许输入1024个字符。 | |||
| create_dataset=创建数据集 | |||
| create_dataset_fail=创建数据集失败。 | |||
| query_dataset_fail=查询数据集失败 | |||
| show_dataset=数据集 | |||
| edit_dataset=编辑数据集 | |||
| update_dataset=更新数据集 | |||
| @@ -301,6 +301,10 @@ func ExploreDatasets(ctx *context.Context) { | |||
| orderBy = models.SearchOrderBySizeReverse | |||
| case "downloadtimes": | |||
| orderBy = models.SearchOrderByDownloadTimes | |||
| case "moststars": | |||
| orderBy = models.SearchOrderByStarsReverse | |||
| case "feweststars": | |||
| orderBy = models.SearchOrderByStars | |||
| default: | |||
| ctx.Data["SortType"] = "recentupdate" | |||
| orderBy = models.SearchOrderByRecentUpdated | |||
| @@ -2,8 +2,10 @@ package repo | |||
| import ( | |||
| "net/http" | |||
| "regexp" | |||
| "sort" | |||
| "strconv" | |||
| "unicode/utf8" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/auth" | |||
| @@ -14,9 +16,12 @@ import ( | |||
| ) | |||
| const ( | |||
| tplIndex base.TplName = "repo/datasets/index" | |||
| tplIndex base.TplName = "repo/datasets/index" | |||
| tplDatasetCreate base.TplName = "repo/datasets/create" | |||
| ) | |||
| var titlePattern = regexp.MustCompile(`^[A-Za-z0-9-_\\.]{1,100}$`) | |||
| // MustEnableDataset check if repository enable internal dataset | |||
| func MustEnableDataset(ctx *context.Context) { | |||
| if !ctx.Repo.CanRead(models.UnitTypeDatasets) { | |||
| @@ -161,22 +166,69 @@ func DatasetIndex(ctx *context.Context) { | |||
| ctx.HTML(200, tplIndex) | |||
| } | |||
| func CreateDataset(ctx *context.Context) { | |||
| MustEnableDataset(ctx) | |||
| ctx.HTML(200, tplDatasetCreate) | |||
| } | |||
| func CreateDatasetPost(ctx *context.Context, form auth.CreateDatasetForm) { | |||
| dataset := &models.Dataset{} | |||
| if !titlePattern.MatchString(form.Title) { | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err"))) | |||
| return | |||
| } | |||
| if utf8.RuneCountInString(form.Description) > 1024 { | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err"))) | |||
| return | |||
| } | |||
| dataset.RepoID = ctx.Repo.Repository.ID | |||
| dataset.UserID = ctx.User.ID | |||
| dataset.Category = form.Category | |||
| dataset.Task = form.Task | |||
| dataset.Title = form.Title | |||
| dataset.License = form.License | |||
| dataset.Description = form.Description | |||
| dataset.DownloadTimes = 0 | |||
| if ctx.Repo.Repository.IsPrivate { | |||
| dataset.Status = 0 | |||
| } else { | |||
| dataset.Status = 1 | |||
| } | |||
| err := models.CreateDataset(dataset) | |||
| if err != nil { | |||
| log.Error("fail to create dataset", err) | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.create_dataset_fail"))) | |||
| } else { | |||
| ctx.JSON(http.StatusOK, models.BaseOKMessage) | |||
| } | |||
| } | |||
| func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) { | |||
| ctx.Data["PageIsDataset"] = true | |||
| ctx.Data["Title"] = ctx.Tr("dataset.edit_dataset") | |||
| if !titlePattern.MatchString(form.Title) { | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err"))) | |||
| return | |||
| } | |||
| if utf8.RuneCountInString(form.Description) > 1024 { | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err"))) | |||
| return | |||
| } | |||
| rel, err := models.GetDatasetByID(form.ID) | |||
| ctx.Data["dataset"] = rel | |||
| if err != nil { | |||
| ctx.ServerError("GetDataset", err) | |||
| return | |||
| } | |||
| if ctx.HasError() { | |||
| ctx.Data["Error"] = true | |||
| ctx.HTML(200, tplIndex) | |||
| log.Error("failed to query dataset", err) | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.query_dataset_fail"))) | |||
| return | |||
| } | |||
| @@ -186,11 +238,9 @@ func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) { | |||
| rel.Task = form.Task | |||
| rel.License = form.License | |||
| if err = models.UpdateDataset(models.DefaultDBContext(), rel); err != nil { | |||
| ctx.Data["Error"] = true | |||
| ctx.HTML(200, tplIndex) | |||
| log.Error("%v", err) | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.query_dataset_fail"))) | |||
| } | |||
| ctx.Redirect(ctx.Repo.RepoLink + "/datasets?type=" + form.Type) | |||
| ctx.JSON(http.StatusOK, models.BaseOKMessage) | |||
| } | |||
| func DatasetAction(ctx *context.Context) { | |||
| @@ -204,9 +254,9 @@ func DatasetAction(ctx *context.Context) { | |||
| } | |||
| if err != nil { | |||
| ctx.JSON(http.StatusOK, models.BaseMessage{1, ctx.Tr("repo.star_fail", ctx.Params(":action"))}) | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.star_fail", ctx.Params(":action")))) | |||
| } else { | |||
| ctx.JSON(http.StatusOK, models.BaseMessageOK) | |||
| ctx.JSON(http.StatusOK, models.BaseOKMessage) | |||
| } | |||
| } | |||
| @@ -979,6 +979,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Group("/datasets", func() { | |||
| m.Get("", reqRepoDatasetReader, repo.DatasetIndex) | |||
| m.Put("/:id/:action", reqRepoDatasetReader, repo.DatasetAction) | |||
| m.Get("/create", reqRepoDatasetWriter, repo.CreateDataset) | |||
| m.Post("/create", reqRepoDatasetWriter, bindIgnErr(auth.CreateDatasetForm{}), repo.CreateDatasetPost) | |||
| m.Post("", reqRepoDatasetWriter, bindIgnErr(auth.EditDatasetForm{}), repo.EditDatasetPost) | |||
| m.Group("/dirs", func() { | |||