| @@ -8,20 +8,6 @@ import ( | |||||
| "xorm.io/builder" | "xorm.io/builder" | ||||
| ) | ) | ||||
| const ( | |||||
| DatasetStatusPrivate int = iota | |||||
| DatasetStatusPublic | |||||
| DatasetStatusDeleted | |||||
| ) | |||||
| type DatasetList []*Dataset | |||||
| type SearchDatasetOptions struct { | |||||
| ListOptions | |||||
| Keyword string | |||||
| OwnerID int64 | |||||
| IsPublic bool | |||||
| } | |||||
| type Dataset struct { | type Dataset struct { | ||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| Title string `xorm:"INDEX NOT NULL"` | Title string `xorm:"INDEX NOT NULL"` | ||||
| @@ -39,51 +25,28 @@ type Dataset struct { | |||||
| Attachments []*Attachment `xorm:"-"` | Attachments []*Attachment `xorm:"-"` | ||||
| } | } | ||||
| func CreateDataset(dataset *Dataset) (err error) { | |||||
| if _, err = x.Insert(dataset); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // AddDatasetAttachments adds a Dataset attachments | |||||
| func AddDatasetAttachments(DatasetID int64, attachmentUUIDs []string) (err error) { | |||||
| // Check attachments | |||||
| attachments, err := GetAttachmentsByUUIDs(attachmentUUIDs) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %v", attachmentUUIDs, err) | |||||
| } | |||||
| type DatasetList []*Dataset | |||||
| for i := range attachments { | |||||
| attachments[i].DatasetID = DatasetID | |||||
| // No assign value could be 0, so ignore AllCols(). | |||||
| if _, err = x.ID(attachments[i].ID).Update(attachments[i]); err != nil { | |||||
| return fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err) | |||||
| } | |||||
| } | |||||
| const ( | |||||
| DatasetStatusPrivate int = iota | |||||
| DatasetStatusPublic | |||||
| DatasetStatusDeleted | |||||
| ) | |||||
| return | |||||
| type SearchDatasetOptions struct { | |||||
| Keyword string | |||||
| OwnerID int64 | |||||
| IsPublic bool | |||||
| ListOptions | |||||
| SearchOrderBy | |||||
| } | } | ||||
| // GetDatasetByID returns Dataset with given ID. | |||||
| func GetDatasetByID(id int64) (*Dataset, error) { | |||||
| rel := new(Dataset) | |||||
| has, err := x. | |||||
| ID(id). | |||||
| Get(rel) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrDatasetNotExist{id} | |||||
| func CreateDataset(dataset *Dataset) (err error) { | |||||
| if _, err = x.Insert(dataset); err != nil { | |||||
| return err | |||||
| } | } | ||||
| return rel, nil | |||||
| } | |||||
| func UpdateDataset(ctx DBContext, rel *Dataset) error { | |||||
| _, err := ctx.e.ID(rel.ID).AllCols().Update(rel) | |||||
| return err | |||||
| return nil | |||||
| } | } | ||||
| func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) { | func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) { | ||||
| @@ -120,7 +83,7 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da | |||||
| // } | // } | ||||
| repos := make(DatasetList, 0, opts.PageSize) | repos := make(DatasetList, 0, opts.PageSize) | ||||
| sess.Where(cond) | |||||
| sess.Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||||
| if opts.PageSize > 0 { | if opts.PageSize > 0 { | ||||
| sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | ||||
| } | } | ||||
| @@ -190,3 +153,42 @@ func geDatasetAttachments(e Engine, rels ...*Dataset) (err error) { | |||||
| return | return | ||||
| } | } | ||||
| // AddDatasetAttachments adds a Dataset attachments | |||||
| func AddDatasetAttachments(DatasetID int64, attachmentUUIDs []string) (err error) { | |||||
| // Check attachments | |||||
| attachments, err := GetAttachmentsByUUIDs(attachmentUUIDs) | |||||
| if err != nil { | |||||
| return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %v", attachmentUUIDs, err) | |||||
| } | |||||
| for i := range attachments { | |||||
| attachments[i].DatasetID = DatasetID | |||||
| // No assign value could be 0, so ignore AllCols(). | |||||
| if _, err = x.ID(attachments[i].ID).Update(attachments[i]); err != nil { | |||||
| return fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err) | |||||
| } | |||||
| } | |||||
| return | |||||
| } | |||||
| func UpdateDataset(ctx DBContext, rel *Dataset) error { | |||||
| _, err := ctx.e.ID(rel.ID).AllCols().Update(rel) | |||||
| return err | |||||
| } | |||||
| // GetDatasetByID returns Dataset with given ID. | |||||
| func GetDatasetByID(id int64) (*Dataset, error) { | |||||
| rel := new(Dataset) | |||||
| has, err := x. | |||||
| ID(id). | |||||
| Get(rel) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrDatasetNotExist{id} | |||||
| } | |||||
| return rel, nil | |||||
| } | |||||
| @@ -17,6 +17,10 @@ type CreateDatasetForm struct { | |||||
| Files []string | Files []string | ||||
| } | } | ||||
| func (f *CreateDatasetForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| type EditDatasetForm struct { | type EditDatasetForm struct { | ||||
| ID int64 `binding:"Required"` | ID int64 `binding:"Required"` | ||||
| Title string `binding:"Required"` | Title string `binding:"Required"` | ||||
| @@ -28,8 +32,3 @@ type EditDatasetForm struct { | |||||
| ReleaseID int64 `xorm:"INDEX"` | ReleaseID int64 `xorm:"INDEX"` | ||||
| Files []string | Files []string | ||||
| } | } | ||||
| // Validate validates the fields | |||||
| func (f *CreateDatasetForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| @@ -23,20 +23,62 @@ type ListOptions struct { | |||||
| } | } | ||||
| func MyList(ctx *context.Context) { | func MyList(ctx *context.Context) { | ||||
| var ( | |||||
| datasets []*models.Dataset | |||||
| count int64 | |||||
| err error | |||||
| orderBy models.SearchOrderBy | |||||
| ) | |||||
| ctxUser := ctx.User | ctxUser := ctx.User | ||||
| page := ctx.QueryInt("page") | page := ctx.QueryInt("page") | ||||
| if page <= 0 { | if page <= 0 { | ||||
| page = 1 | page = 1 | ||||
| } | } | ||||
| switch ctx.Query("sort") { | |||||
| case "newest": | |||||
| orderBy = models.SearchOrderByNewest | |||||
| case "oldest": | |||||
| orderBy = models.SearchOrderByOldest | |||||
| case "recentupdate": | |||||
| orderBy = models.SearchOrderByRecentUpdated | |||||
| case "leastupdate": | |||||
| orderBy = models.SearchOrderByLeastUpdated | |||||
| case "reversealphabetically": | |||||
| orderBy = models.SearchOrderByAlphabeticallyReverse | |||||
| case "alphabetically": | |||||
| orderBy = models.SearchOrderByAlphabetically | |||||
| case "reversesize": | |||||
| orderBy = models.SearchOrderBySizeReverse | |||||
| case "size": | |||||
| orderBy = models.SearchOrderBySize | |||||
| case "moststars": | |||||
| orderBy = models.SearchOrderByStarsReverse | |||||
| case "feweststars": | |||||
| orderBy = models.SearchOrderByStars | |||||
| case "mostforks": | |||||
| orderBy = models.SearchOrderByForksReverse | |||||
| case "fewestforks": | |||||
| orderBy = models.SearchOrderByForks | |||||
| default: | |||||
| ctx.Data["SortType"] = "recentupdate" | |||||
| orderBy = models.SearchOrderByRecentUpdated | |||||
| } | |||||
| datasetSearchOptions := &models.SearchDatasetOptions{ | datasetSearchOptions := &models.SearchDatasetOptions{ | ||||
| OwnerID: ctxUser.ID, | |||||
| Keyword: ctx.QueryTrim("keyword"), | |||||
| OwnerID: ctxUser.ID, | |||||
| IsPublic: false, | |||||
| SearchOrderBy: orderBy, | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.ExplorePagingNum, | |||||
| }, | |||||
| } | } | ||||
| var ( | |||||
| datasets []*models.Dataset | |||||
| count int64 | |||||
| err error | |||||
| ) | |||||
| if len(datasetSearchOptions.SearchOrderBy) == 0 { | |||||
| datasetSearchOptions.SearchOrderBy = models.SearchOrderByAlphabetically | |||||
| } | |||||
| datasets, count, err = models.SearchDataset(datasetSearchOptions) | datasets, count, err = models.SearchDataset(datasetSearchOptions) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -209,6 +209,27 @@ func Profile(ctx *context.Context) { | |||||
| } | } | ||||
| total = int(count) | total = int(count) | ||||
| case "datasets": | |||||
| datasetSearchOptions := &models.SearchDatasetOptions{ | |||||
| Keyword: keyword, | |||||
| OwnerID: ctxUser.ID, | |||||
| SearchOrderBy: orderBy, | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.ExplorePagingNum, | |||||
| }, | |||||
| } | |||||
| if len(datasetSearchOptions.SearchOrderBy) == 0 { | |||||
| datasetSearchOptions.SearchOrderBy = models.SearchOrderByAlphabetically | |||||
| } | |||||
| datasets, count, err := models.SearchDataset(datasetSearchOptions) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchDatasets", err) | |||||
| } | |||||
| total = int(count) | |||||
| ctx.Data["datasets"] = datasets | |||||
| default: | default: | ||||
| repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ | repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ | ||||
| ListOptions: models.ListOptions{ | ListOptions: models.ListOptions{ | ||||
| @@ -1,4 +1,4 @@ | |||||
| <div class="ui repository list"> | |||||
| <div class="ui dataset list"> | |||||
| {{range .datasets}} | {{range .datasets}} | ||||
| <div class="item"> | <div class="item"> | ||||
| <div class="ui header"> | <div class="ui header"> | ||||
| @@ -14,91 +14,9 @@ | |||||
| <p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSinceUnix .UpdatedUnix $.i18n.Lang}}</p> | <p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSinceUnix .UpdatedUnix $.i18n.Lang}}</p> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} | |||||
| <div class="item"> | |||||
| <div class="ui header"> | |||||
| <a class="name" href="{{.Link}}"> | |||||
| 人脸分析 | |||||
| </a> | |||||
| <div class="ui right metas"> | |||||
| <span class="text grey">{{svg "octicon-flame" 16}} 24</span> | |||||
| </div> | |||||
| </div> | |||||
| <div class="description"> | |||||
| <a><div class="ui small label topic">人脸分析100张经典样例图片</div></a> | |||||
| <p class="time">{{$.i18n.Tr "org.repo_updated"}} 2019-02-01</p> | |||||
| </div> | |||||
| </div> | |||||
| <div class="item"> | |||||
| <div class="ui header"> | |||||
| <a class="name" href="{{.Link}}"> | |||||
| 人脸分析 | |||||
| </a> | |||||
| <div class="ui right metas"> | |||||
| <span class="text grey">{{svg "octicon-flame" 16}} 24</span> | |||||
| </div> | |||||
| {{else}} | |||||
| <div> | |||||
| {{$.i18n.Tr "explore.repo_no_results"}} | |||||
| </div> | </div> | ||||
| <div class="description"> | |||||
| <a><div class="ui small label topic">人脸分析100张经典样例图片</div></a> | |||||
| <p class="time">{{$.i18n.Tr "org.repo_updated"}} 2019-02-01</p> | |||||
| </div> | |||||
| </div> | |||||
| <div class="item"> | |||||
| <div class="ui header"> | |||||
| <a class="name" href="{{.Link}}"> | |||||
| 人脸分析 | |||||
| </a> | |||||
| <div class="ui right metas"> | |||||
| <span class="text grey">{{svg "octicon-flame" 16}} 12</span> | |||||
| </div> | |||||
| </div> | |||||
| <div class="description"> | |||||
| <a><div class="ui small label topic">人脸分析100张经典样例图片</div></a> | |||||
| <p class="time">{{$.i18n.Tr "org.repo_updated"}} 2019-02-01</p> | |||||
| </div> | |||||
| </div> | |||||
| {{range .Repos}} | |||||
| <div class="item"> | |||||
| <div class="ui header"> | |||||
| {{if .RelAvatarLink}} | |||||
| <img class="ui avatar image" src="{{.RelAvatarLink}}"> | |||||
| {{end}} | |||||
| <a class="name" href="{{.Link}}"> | |||||
| {{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}} | |||||
| {{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} | |||||
| </a> | |||||
| {{if .IsPrivate}} | |||||
| <span class="middle text gold">{{svg "octicon-lock" 16}}</span> | |||||
| {{else if .IsFork}} | |||||
| <span class="middle">{{svg "octicon-repo-forked" 16}}</span> | |||||
| {{end}} | |||||
| <div class="ui right metas"> | |||||
| {{if .PrimaryLanguage }} | |||||
| <span class="text grey"><i class="color-icon" style="background-color: {{.PrimaryLanguage.Color}}"></i>{{ .PrimaryLanguage.Language }}</span> | |||||
| {{end}} | |||||
| <span class="text grey">{{svg "octicon-flame" 16}} {{.NumStars}}</span> | |||||
| </div> | |||||
| </div> | |||||
| <div class="description"> | |||||
| {{if .DescriptionHTML}}<p>{{.DescriptionHTML}}</p>{{end}} | |||||
| {{if .Topics }} | |||||
| <div class="ui tags"> | |||||
| {{range .Topics}} | |||||
| {{if ne . "" }}<a href="{{AppSubUrl}}/explore/repos?q={{.}}&topic=1"><div class="ui small label topic">{{.}}</div></a>{{end}} | |||||
| {{end}} | |||||
| </div> | |||||
| {{end}} | |||||
| <p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSinceUnix .UpdatedUnix $.i18n.Lang}}</p> | |||||
| </div> | |||||
| </div> | |||||
| {{else}} | |||||
| <div> | |||||
| {{$.i18n.Tr "explore.repo_no_results"}} | |||||
| </div> | |||||
| {{end}} | |||||
| {{end}} | |||||
| </div> | </div> | ||||
| @@ -131,7 +131,9 @@ | |||||
| {{else if eq .TabName "followers"}} | {{else if eq .TabName "followers"}} | ||||
| {{template "repo/user_cards" .}} | {{template "repo/user_cards" .}} | ||||
| {{else if eq .TabName "datasets"}} | {{else if eq .TabName "datasets"}} | ||||
| {{template "datasets/dataset_search" .}} | |||||
| {{template "datasets/dataset_list" .}} | {{template "datasets/dataset_list" .}} | ||||
| {{template "base/paginate" .}} | |||||
| {{else}} | {{else}} | ||||
| {{template "explore/repo_search" .}} | {{template "explore/repo_search" .}} | ||||
| {{template "explore/repo_list" .}} | {{template "explore/repo_list" .}} | ||||
| @@ -53,7 +53,6 @@ | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| #dataset-list { | #dataset-list { | ||||
| border-top: 1px solid #dddddd; | border-top: 1px solid #dddddd; | ||||
| margin-top: 20px; | margin-top: 20px; | ||||
| @@ -135,4 +134,47 @@ | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| .ui.dataset.list { | |||||
| .item { | |||||
| padding-bottom: 25px; | |||||
| &:not(:first-child) { | |||||
| border-top: 1px solid #eeeeee; | |||||
| padding-top: 25px; | |||||
| } | |||||
| .ui.header { | |||||
| font-size: 1.5rem; | |||||
| padding-bottom: 10px; | |||||
| .name { | |||||
| word-break: break-all; | |||||
| } | |||||
| .metas { | |||||
| color: #888888; | |||||
| font-size: 14px; | |||||
| font-weight: normal; | |||||
| span:not(:last-child) { | |||||
| margin-right: 5px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .time { | |||||
| font-size: 12px; | |||||
| color: #808080; | |||||
| } | |||||
| .ui.tags { | |||||
| margin-bottom: 1em; | |||||
| } | |||||
| .ui.avatar.image { | |||||
| width: 24px; | |||||
| height: 24px; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -17,3 +17,4 @@ | |||||
| @import "_admin"; | @import "_admin"; | ||||
| @import "_explore"; | @import "_explore"; | ||||
| @import "_review"; | @import "_review"; | ||||
| @import "_dataset"; | |||||