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

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