You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

dataset.go 11 kB

3 years ago
5 years ago
3 years ago
5 years ago
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
5 years ago
3 years ago
3 years ago
3 years ago
5 years ago
5 years ago
3 years ago
3 years ago
5 years ago
3 years ago
3 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
3 years ago
5 years ago
3 years ago
5 years ago
5 years ago
5 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. package models
  2. import (
  3. "code.gitea.io/gitea/modules/log"
  4. "errors"
  5. "fmt"
  6. "sort"
  7. "code.gitea.io/gitea/modules/timeutil"
  8. "xorm.io/builder"
  9. )
  10. const (
  11. DatasetStatusPrivate int32 = iota
  12. DatasetStatusPublic
  13. DatasetStatusDeleted
  14. )
  15. type Dataset struct {
  16. ID int64 `xorm:"pk autoincr"`
  17. Title string `xorm:"INDEX NOT NULL"`
  18. Status int32 `xorm:"INDEX"` // normal_private: 0, pulbic: 1, is_delete: 2
  19. Category string
  20. Description string `xorm:"TEXT"`
  21. DownloadTimes int64
  22. NumStars int `xorm:"INDEX NOT NULL DEFAULT 0"`
  23. Recommend bool `xorm:"INDEX NOT NULL DEFAULT false"`
  24. License string
  25. Task string
  26. ReleaseID int64 `xorm:"INDEX"`
  27. UserID int64 `xorm:"INDEX"`
  28. RepoID int64 `xorm:"INDEX"`
  29. Repo *Repository `xorm:"-"`
  30. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  31. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  32. User *User `xorm:"-"`
  33. Attachments []*Attachment `xorm:"-"`
  34. }
  35. type DatasetWithStar struct {
  36. Dataset
  37. IsStaring bool
  38. }
  39. func (d *Dataset) IsPrivate() bool {
  40. switch d.Status {
  41. case DatasetStatusPrivate:
  42. return true
  43. case DatasetStatusPublic:
  44. return false
  45. case DatasetStatusDeleted:
  46. return false
  47. default:
  48. return false
  49. }
  50. }
  51. type DatasetList []*Dataset
  52. func (datasets DatasetList) loadAttributes(e Engine) error {
  53. if len(datasets) == 0 {
  54. return nil
  55. }
  56. set := make(map[int64]struct{})
  57. userIdSet := make(map[int64]struct{})
  58. datasetIDs := make([]int64, len(datasets))
  59. for i := range datasets {
  60. userIdSet[datasets[i].UserID] = struct{}{}
  61. set[datasets[i].RepoID] = struct{}{}
  62. datasetIDs[i] = datasets[i].ID
  63. }
  64. // Load owners.
  65. users := make(map[int64]*User, len(userIdSet))
  66. repos := make(map[int64]*Repository, len(set))
  67. if err := e.
  68. Where("id > 0").
  69. In("id", keysInt64(userIdSet)).
  70. Find(&users); err != nil {
  71. return fmt.Errorf("find users: %v", err)
  72. }
  73. if err := e.
  74. Where("id > 0").
  75. In("id", keysInt64(set)).
  76. Find(&repos); err != nil {
  77. return fmt.Errorf("find repos: %v", err)
  78. }
  79. for i := range datasets {
  80. datasets[i].User = users[datasets[i].UserID]
  81. datasets[i].Repo = repos[datasets[i].RepoID]
  82. }
  83. return nil
  84. }
  85. type SearchDatasetOptions struct {
  86. Keyword string
  87. OwnerID int64
  88. RepoID int64
  89. IncludePublic bool
  90. RecommendOnly bool
  91. Category string
  92. Task string
  93. License string
  94. ListOptions
  95. SearchOrderBy
  96. IsOwner bool
  97. }
  98. func CreateDataset(dataset *Dataset) (err error) {
  99. sess := x.NewSession()
  100. defer sess.Close()
  101. if err := sess.Begin(); err != nil {
  102. return err
  103. }
  104. datasetByRepoId := &Dataset{RepoID: dataset.RepoID}
  105. has, err := sess.Get(datasetByRepoId)
  106. if err != nil {
  107. return err
  108. }
  109. if has {
  110. return fmt.Errorf("The dataset already exists.")
  111. }
  112. if _, err = sess.Insert(dataset); err != nil {
  113. return err
  114. }
  115. return sess.Commit()
  116. }
  117. func RecommendDataset(dataSetId int64, recommend bool) error {
  118. dataset := Dataset{Recommend: recommend}
  119. _, err := x.ID(dataSetId).Cols("recommend").Update(dataset)
  120. return err
  121. }
  122. func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) {
  123. cond := SearchDatasetCondition(opts)
  124. return SearchDatasetByCondition(opts, cond)
  125. }
  126. func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond {
  127. var cond = builder.NewCond()
  128. cond = cond.And(builder.Neq{"dataset.status": DatasetStatusDeleted})
  129. cond = generateFilterCond(opts, cond)
  130. if opts.RepoID > 0 {
  131. cond = cond.And(builder.Eq{"dataset.repo_id": opts.RepoID})
  132. }
  133. if opts.IncludePublic {
  134. cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
  135. cond = cond.And(builder.Eq{"attachment.is_private": false})
  136. if opts.OwnerID > 0 {
  137. subCon := builder.NewCond()
  138. subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID})
  139. subCon = generateFilterCond(opts, subCon)
  140. cond = cond.Or(subCon)
  141. }
  142. } else if opts.OwnerID > 0 {
  143. cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID})
  144. if !opts.IsOwner {
  145. cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
  146. cond = cond.And(builder.Eq{"attachment.is_private": false})
  147. }
  148. }
  149. return cond
  150. }
  151. func generateFilterCond(opts *SearchDatasetOptions, cond builder.Cond) builder.Cond {
  152. if len(opts.Keyword) > 0 {
  153. cond = cond.And(builder.Or(builder.Like{"dataset.title", opts.Keyword}, builder.Like{"dataset.description", opts.Keyword}))
  154. }
  155. if len(opts.Category) > 0 {
  156. cond = cond.And(builder.Eq{"dataset.category": opts.Category})
  157. }
  158. if len(opts.Task) > 0 {
  159. cond = cond.And(builder.Eq{"dataset.task": opts.Task})
  160. }
  161. if len(opts.License) > 0 {
  162. cond = cond.And(builder.Eq{"dataset.license": opts.License})
  163. }
  164. if opts.RecommendOnly {
  165. cond = cond.And(builder.Eq{"dataset.recommend": opts.RecommendOnly})
  166. }
  167. return cond
  168. }
  169. func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (DatasetList, int64, error) {
  170. if opts.Page <= 0 {
  171. opts.Page = 1
  172. }
  173. var err error
  174. sess := x.NewSession()
  175. defer sess.Close()
  176. datasets := make(DatasetList, 0, opts.PageSize)
  177. 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,dataset.recommend"
  178. count, err := sess.Distinct("dataset.id").Join("INNER", "repository", "repository.id = dataset.repo_id").
  179. Join("INNER", "attachment", "attachment.dataset_id=dataset.id").
  180. Where(cond).Count(new(Dataset))
  181. if err != nil {
  182. return nil, 0, fmt.Errorf("Count: %v", err)
  183. }
  184. sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id").
  185. Join("INNER", "attachment", "attachment.dataset_id=dataset.id").
  186. Where(cond).OrderBy(opts.SearchOrderBy.String())
  187. if opts.PageSize > 0 {
  188. sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
  189. }
  190. if err = sess.Find(&datasets); err != nil {
  191. return nil, 0, fmt.Errorf("Dataset: %v", err)
  192. }
  193. if err = datasets.loadAttributes(sess); err != nil {
  194. return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
  195. }
  196. return datasets, count, nil
  197. }
  198. type datasetMetaSearch struct {
  199. ID []int64
  200. Rel []*Dataset
  201. }
  202. func (s datasetMetaSearch) Len() int {
  203. return len(s.ID)
  204. }
  205. func (s datasetMetaSearch) Swap(i, j int) {
  206. s.ID[i], s.ID[j] = s.ID[j], s.ID[i]
  207. s.Rel[i], s.Rel[j] = s.Rel[j], s.Rel[i]
  208. }
  209. func (s datasetMetaSearch) Less(i, j int) bool {
  210. return s.ID[i] < s.ID[j]
  211. }
  212. func GetDatasetAttachments(typeCloudBrain int, isSigned bool, user *User, rels ...*Dataset) (err error) {
  213. return getDatasetAttachments(x, typeCloudBrain, isSigned, user, rels...)
  214. }
  215. func getDatasetAttachments(e Engine, typeCloudBrain int, isSigned bool, user *User, rels ...*Dataset) (err error) {
  216. if len(rels) == 0 {
  217. return
  218. }
  219. // To keep this efficient as possible sort all datasets by id,
  220. // select attachments by dataset id,
  221. // then merge join them
  222. // Sort
  223. var sortedRels = datasetMetaSearch{ID: make([]int64, len(rels)), Rel: make([]*Dataset, len(rels))}
  224. var attachments []*Attachment
  225. for index, element := range rels {
  226. element.Attachments = []*Attachment{}
  227. sortedRels.ID[index] = element.ID
  228. sortedRels.Rel[index] = element
  229. }
  230. sort.Sort(sortedRels)
  231. // Select attachments
  232. if typeCloudBrain == -1 {
  233. err = e.
  234. Asc("dataset_id").
  235. In("dataset_id", sortedRels.ID).
  236. Find(&attachments, Attachment{})
  237. if err != nil {
  238. return err
  239. }
  240. } else {
  241. err = e.
  242. Asc("dataset_id").
  243. In("dataset_id", sortedRels.ID).
  244. And("type = ?", typeCloudBrain).
  245. Find(&attachments, Attachment{})
  246. if err != nil {
  247. return err
  248. }
  249. }
  250. // merge join
  251. var currentIndex = 0
  252. for _, attachment := range attachments {
  253. for sortedRels.ID[currentIndex] < attachment.DatasetID {
  254. currentIndex++
  255. }
  256. fileChunks := make([]*FileChunk, 0, 10)
  257. err = e.
  258. Where("uuid = ?", attachment.UUID).
  259. Find(&fileChunks)
  260. if err != nil {
  261. return err
  262. }
  263. if len(fileChunks) > 0 {
  264. attachment.Md5 = fileChunks[0].Md5
  265. } else {
  266. log.Error("has attachment record, but has no file_chunk record")
  267. attachment.Md5 = "no_record"
  268. }
  269. attachment.CanDel = CanDelAttachment(isSigned, user, attachment)
  270. sortedRels.Rel[currentIndex].Attachments = append(sortedRels.Rel[currentIndex].Attachments, attachment)
  271. }
  272. return
  273. }
  274. // AddDatasetAttachments adds a Dataset attachments
  275. func AddDatasetAttachments(DatasetID int64, attachmentUUIDs []string) (err error) {
  276. // Check attachments
  277. attachments, err := GetAttachmentsByUUIDs(attachmentUUIDs)
  278. if err != nil {
  279. return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %v", attachmentUUIDs, err)
  280. }
  281. for i := range attachments {
  282. attachments[i].DatasetID = DatasetID
  283. // No assign value could be 0, so ignore AllCols().
  284. if _, err = x.ID(attachments[i].ID).Update(attachments[i]); err != nil {
  285. return fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err)
  286. }
  287. }
  288. return
  289. }
  290. func UpdateDataset(ctx DBContext, rel *Dataset) error {
  291. _, err := ctx.e.ID(rel.ID).AllCols().Update(rel)
  292. return err
  293. }
  294. // GetDatasetByID returns Dataset with given ID.
  295. func GetDatasetByID(id int64) (*Dataset, error) {
  296. rel := new(Dataset)
  297. has, err := x.
  298. ID(id).
  299. Get(rel)
  300. if err != nil {
  301. return nil, err
  302. } else if !has {
  303. return nil, ErrDatasetNotExist{id}
  304. }
  305. return rel, nil
  306. }
  307. func GetDatasetByRepo(repo *Repository) (*Dataset, error) {
  308. dataset := &Dataset{RepoID: repo.ID}
  309. has, err := x.Get(dataset)
  310. if err != nil {
  311. return nil, err
  312. }
  313. if has {
  314. return dataset, nil
  315. } else {
  316. return nil, ErrNotExist{repo.ID}
  317. }
  318. }
  319. func GetDatasetStarByUser(user *User) ([]*DatasetStar, error) {
  320. datasetStars := make([]*DatasetStar, 0)
  321. err := x.Cols("id", "uid", "dataset_id", "created_unix").Where("uid=?", user.ID).Find(&datasetStars)
  322. return datasetStars, err
  323. }
  324. func DeleteDataset(datasetID int64, uid int64) error {
  325. var err error
  326. sess := x.NewSession()
  327. defer sess.Close()
  328. if err = sess.Begin(); err != nil {
  329. return err
  330. }
  331. dataset := &Dataset{ID: datasetID, UserID: uid}
  332. has, err := sess.Get(dataset)
  333. if err != nil {
  334. return err
  335. } else if !has {
  336. return errors.New("not found")
  337. }
  338. if cnt, err := sess.ID(datasetID).Delete(new(Dataset)); err != nil {
  339. return err
  340. } else if cnt != 1 {
  341. return errors.New("not found")
  342. }
  343. if err = sess.Commit(); err != nil {
  344. sess.Close()
  345. return fmt.Errorf("Commit: %v", err)
  346. }
  347. return nil
  348. }
  349. func GetOwnerDatasetByID(id int64, user *User) (*Dataset, error) {
  350. dataset, err := GetDatasetByID(id)
  351. if err != nil {
  352. return nil, err
  353. }
  354. if !dataset.IsPrivate() {
  355. return dataset, nil
  356. }
  357. if dataset.IsPrivate() && user != nil && user.ID == dataset.UserID {
  358. return dataset, nil
  359. }
  360. return nil, errors.New("dataset not fount")
  361. }
  362. func IncreaseDownloadCount(datasetID int64) error {
  363. // Update download count.
  364. if _, err := x.Exec("UPDATE `dataset` SET download_times=download_times+1 WHERE id=?", datasetID); err != nil {
  365. return fmt.Errorf("increase dataset count: %v", err)
  366. }
  367. return nil
  368. }