@@ -8,20 +8,6 @@ import ( | |||
"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 { | |||
ID int64 `xorm:"pk autoincr"` | |||
Title string `xorm:"INDEX NOT NULL"` | |||
@@ -39,51 +25,28 @@ type Dataset struct { | |||
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) { | |||
@@ -120,7 +83,7 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da | |||
// } | |||
repos := make(DatasetList, 0, opts.PageSize) | |||
sess.Where(cond) | |||
sess.Where(cond).OrderBy(opts.SearchOrderBy.String()) | |||
if opts.PageSize > 0 { | |||
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | |||
} | |||
@@ -190,3 +153,42 @@ func geDatasetAttachments(e Engine, rels ...*Dataset) (err error) { | |||
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 | |||
} | |||
func (f *CreateDatasetForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
return validate(errs, ctx.Data, f, ctx.Locale) | |||
} | |||
type EditDatasetForm struct { | |||
ID int64 `binding:"Required"` | |||
Title string `binding:"Required"` | |||
@@ -28,8 +32,3 @@ type EditDatasetForm struct { | |||
ReleaseID int64 `xorm:"INDEX"` | |||
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) { | |||
var ( | |||
datasets []*models.Dataset | |||
count int64 | |||
err error | |||
orderBy models.SearchOrderBy | |||
) | |||
ctxUser := ctx.User | |||
page := ctx.QueryInt("page") | |||
if page <= 0 { | |||
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{ | |||
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) | |||
if err != nil { | |||
@@ -209,6 +209,27 @@ func Profile(ctx *context.Context) { | |||
} | |||
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: | |||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ | |||
ListOptions: models.ListOptions{ | |||
@@ -1,4 +1,4 @@ | |||
<div class="ui repository list"> | |||
<div class="ui dataset list"> | |||
{{range .datasets}} | |||
<div class="item"> | |||
<div class="ui header"> | |||
@@ -14,91 +14,9 @@ | |||
<p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSinceUnix .UpdatedUnix $.i18n.Lang}}</p> | |||
</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 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> |
@@ -131,7 +131,9 @@ | |||
{{else if eq .TabName "followers"}} | |||
{{template "repo/user_cards" .}} | |||
{{else if eq .TabName "datasets"}} | |||
{{template "datasets/dataset_search" .}} | |||
{{template "datasets/dataset_list" .}} | |||
{{template "base/paginate" .}} | |||
{{else}} | |||
{{template "explore/repo_search" .}} | |||
{{template "explore/repo_list" .}} | |||
@@ -53,7 +53,6 @@ | |||
} | |||
} | |||
} | |||
#dataset-list { | |||
border-top: 1px solid #dddddd; | |||
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 "_explore"; | |||
@import "_review"; | |||
@import "_dataset"; |