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.

cloudbrain_image.go 14 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. package models
  2. import (
  3. "fmt"
  4. "strings"
  5. "unicode/utf8"
  6. "xorm.io/builder"
  7. "code.gitea.io/gitea/modules/timeutil"
  8. )
  9. const RECOMMOND_TYPE = 5
  10. const NORMAL_TYPE = 0
  11. const IMAGE_STATUS_COMMIT = 0
  12. const IMAGE_STATUS_SUCCESS = 1
  13. const IMAGE_STATUS_Failed = 2
  14. type Image struct {
  15. ID int64 `xorm:"pk autoincr" json:"id"`
  16. Type int `xorm:"INDEX NOT NULL" json:"type"` //0 normal 5官方推荐,中间值保留为后续扩展
  17. CloudbrainType int `xorm:"INDEX NOT NULL" json:"cloudbrainType"` //0 云脑一 1云脑二
  18. UID int64 `xorm:"INDEX NOT NULL" json:"uid"`
  19. IsPrivate bool `xorm:"INDEX NOT NULL" json:"isPrivate"`
  20. Tag string `xorm:"varchar(100) UNIQUE" json:"tag"`
  21. Description string `xorm:"varchar(765)" json:"description"`
  22. Topics []string `xorm:"TEXT JSON" json:"topics"`
  23. Place string `xorm:"varchar(300)" json:"place"`
  24. NumStars int `xorm:"NOT NULL DEFAULT 0" json:"numStars"`
  25. IsStar bool `xorm:"-" json:"isStar"`
  26. UserName string `xorm:"-" json:"userName"`
  27. RelAvatarLink string `xorm:"-" json:"relAvatarLink"`
  28. Status int `xorm:"INDEX NOT NULL DEFAULT 0" json:"status"` //0代表正在提交,1提交完成,2提交失败
  29. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
  30. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
  31. }
  32. type ImageList []*Image
  33. type ImageStar struct {
  34. ID int64 `xorm:"pk autoincr"`
  35. UID int64 `xorm:"UNIQUE(s)"`
  36. ImageID int64 `xorm:"UNIQUE(s)"`
  37. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  38. }
  39. type ImageTopic struct {
  40. ID int64 `xorm:"pk autoincr"`
  41. Name string `xorm:"UNIQUE VARCHAR(105)"`
  42. ImageCount int
  43. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  44. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  45. }
  46. type ImageTopicRelation struct {
  47. ImageID int64 `xorm:"UNIQUE(s)"`
  48. TopicID int64 `xorm:"UNIQUE(s)"`
  49. }
  50. type SearchImageOptions struct {
  51. Keyword string
  52. UID int64
  53. Status int
  54. IncludePublicOnly bool
  55. IncludeOfficialOnly bool
  56. IncludePrivateOnly bool
  57. IncludeStarByMe bool
  58. IncludeCustom bool
  59. IncludeOwnerOnly bool
  60. Topics string
  61. CloudbrainType int
  62. ListOptions
  63. SearchOrderBy
  64. }
  65. type ErrorImageTagExist struct {
  66. Tag string
  67. }
  68. type ErrorImageCommitting struct {
  69. Tag string
  70. }
  71. type ImagesPageResult struct {
  72. Count int64 `json:"count"`
  73. Images []*Image `json:"images"`
  74. }
  75. func (err ErrorImageTagExist) Error() string {
  76. return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
  77. }
  78. func (err ErrorImageCommitting) Error() string {
  79. return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
  80. }
  81. type ErrImageNotExist struct {
  82. ID int64
  83. Tag string
  84. }
  85. func (err ErrImageNotExist) Error() string {
  86. return fmt.Sprintf("Image does not exist [id: %d] [tag: %s]", err.ID, err.Tag)
  87. }
  88. func IsErrorImageCommitting(err error) bool {
  89. _, ok := err.(ErrorImageCommitting)
  90. return ok
  91. }
  92. func IsErrImageNotExist(err error) bool {
  93. _, ok := err.(ErrImageNotExist)
  94. return ok
  95. }
  96. func IsErrImageTagExist(err error) bool {
  97. _, ok := err.(ErrorImageTagExist)
  98. return ok
  99. }
  100. func IsImageExist(tag string) (bool, error) {
  101. return x.Exist(&Image{
  102. Tag: tag,
  103. })
  104. }
  105. func IsImageExistByUser(tag string, uid int64) (bool, error) {
  106. return x.Exist(&Image{
  107. Tag: tag,
  108. UID: uid,
  109. Status: IMAGE_STATUS_SUCCESS,
  110. })
  111. }
  112. type FindImageTopicOptions struct {
  113. ListOptions
  114. ImageID int64
  115. Keyword string
  116. }
  117. func (opts *FindImageTopicOptions) toConds() builder.Cond {
  118. var cond = builder.NewCond()
  119. if opts.ImageID > 0 {
  120. cond = cond.And(builder.Eq{"image_topic_relation.image_id": opts.ImageID})
  121. }
  122. if opts.Keyword != "" {
  123. cond = cond.And(builder.Like{"image_topic.name", strings.ToLower(opts.Keyword)})
  124. }
  125. return cond
  126. }
  127. func GetImageByID(id int64) (*Image, error) {
  128. rel := new(Image)
  129. has, err := x.
  130. ID(id).
  131. Get(rel)
  132. if err != nil {
  133. return nil, err
  134. } else if !has {
  135. return nil, ErrImageNotExist{ID: id}
  136. }
  137. return rel, nil
  138. }
  139. func GetImageByTag(tag string) (*Image, error) {
  140. image := &Image{Tag: tag}
  141. has, err := x.
  142. Get(image)
  143. if err != nil {
  144. return nil, err
  145. } else if !has {
  146. return nil, ErrImageNotExist{Tag: tag}
  147. }
  148. return image, nil
  149. }
  150. func SanitizeAndValidateImageTopics(topics []string) (validTopics []string, invalidTopics []string) {
  151. validTopics = make([]string, 0)
  152. mValidTopics := make(map[string]struct{})
  153. invalidTopics = make([]string, 0)
  154. for _, topic := range topics {
  155. topic = strings.TrimSpace(strings.ToLower(topic))
  156. // ignore empty string
  157. if len(topic) == 0 {
  158. continue
  159. }
  160. // ignore same topic twice
  161. if _, ok := mValidTopics[topic]; ok {
  162. continue
  163. }
  164. if utf8.RuneCountInString(topic) <= 35 {
  165. validTopics = append(validTopics, topic)
  166. mValidTopics[topic] = struct{}{}
  167. } else {
  168. invalidTopics = append(invalidTopics, topic)
  169. }
  170. }
  171. return validTopics, invalidTopics
  172. }
  173. func FindImageTopics(opts *FindImageTopicOptions) (topics []*ImageTopic, err error) {
  174. sess := x.Select("image_topic.*").Where(opts.toConds())
  175. if opts.ImageID > 0 {
  176. sess.Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id")
  177. }
  178. if opts.PageSize != 0 && opts.Page != 0 {
  179. sess = opts.setSessionPagination(sess)
  180. }
  181. return topics, sess.Desc("image_topic.image_count").Find(&topics)
  182. }
  183. func SaveImageTopics(imageID int64, topicNames ...string) error {
  184. topics, err := FindImageTopics(&FindImageTopicOptions{
  185. ImageID: imageID,
  186. })
  187. if err != nil {
  188. return err
  189. }
  190. sess := x.NewSession()
  191. defer sess.Close()
  192. if err := sess.Begin(); err != nil {
  193. return err
  194. }
  195. var addedTopicNames []string
  196. for _, topicName := range topicNames {
  197. if strings.TrimSpace(topicName) == "" {
  198. continue
  199. }
  200. var found bool
  201. for _, t := range topics {
  202. if strings.EqualFold(topicName, t.Name) {
  203. found = true
  204. break
  205. }
  206. }
  207. if !found {
  208. addedTopicNames = append(addedTopicNames, topicName)
  209. }
  210. }
  211. var removeTopics []*ImageTopic
  212. for _, t := range topics {
  213. var found bool
  214. for _, topicName := range topicNames {
  215. if strings.EqualFold(topicName, t.Name) {
  216. found = true
  217. break
  218. }
  219. }
  220. if !found {
  221. removeTopics = append(removeTopics, t)
  222. }
  223. }
  224. for _, topicName := range addedTopicNames {
  225. _, err := addTopicByNameToImage(sess, imageID, topicName)
  226. if err != nil {
  227. return err
  228. }
  229. }
  230. for _, topic := range removeTopics {
  231. err := removeTopicFromImage(sess, imageID, topic)
  232. if err != nil {
  233. return err
  234. }
  235. }
  236. topicNames = make([]string, 0, 25)
  237. if err := sess.Table("image_topic").Cols("name").
  238. Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id").
  239. Where("image_topic_relation.image_id = ?", imageID).Desc("image_topic.image_count").Find(&topicNames); err != nil {
  240. return err
  241. }
  242. if _, err := sess.ID(imageID).Cols("topics").Update(&Image{
  243. Topics: topicNames,
  244. }); err != nil {
  245. return err
  246. }
  247. return sess.Commit()
  248. }
  249. func addTopicByNameToImage(e Engine, imageID int64, topicName string) (*ImageTopic, error) {
  250. var topic ImageTopic
  251. has, err := e.Where("name = ?", topicName).Get(&topic)
  252. if err != nil {
  253. return nil, err
  254. }
  255. if !has {
  256. topic.Name = topicName
  257. topic.ImageCount = 1
  258. if _, err := e.Insert(&topic); err != nil {
  259. return nil, err
  260. }
  261. } else {
  262. topic.ImageCount++
  263. if _, err := e.ID(topic.ID).Cols("image_count").Update(&topic); err != nil {
  264. return nil, err
  265. }
  266. }
  267. if _, err := e.Insert(&ImageTopicRelation{
  268. ImageID: imageID,
  269. TopicID: topic.ID,
  270. }); err != nil {
  271. return nil, err
  272. }
  273. return &topic, nil
  274. }
  275. func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error {
  276. topic.ImageCount--
  277. if _, err := e.ID(topic.ID).Cols("image_count").Update(topic); err != nil {
  278. return err
  279. }
  280. if _, err := e.Delete(&ImageTopicRelation{
  281. ImageID: imageId,
  282. TopicID: topic.ID,
  283. }); err != nil {
  284. return err
  285. }
  286. return nil
  287. }
  288. func SearchImage(opts *SearchImageOptions) (ImageList, int64, error) {
  289. cond := SearchImageCondition(opts)
  290. return SearchImageByCondition(opts, cond)
  291. }
  292. func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
  293. var cond = builder.NewCond()
  294. if len(opts.Keyword) > 0 {
  295. var subQueryCond = builder.NewCond()
  296. for _, v := range strings.Split(opts.Keyword, ",") {
  297. subQueryCond = subQueryCond.Or(builder.Like{"LOWER(image_topic.name)", strings.ToLower(v)})
  298. }
  299. subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
  300. Join("INNER", "image_topic", "image_topic.id = image_topic_relation.topic_id").
  301. Where(subQueryCond).
  302. GroupBy("image_topic_relation.image_id")
  303. var keywordCond = builder.In("id", subQuery)
  304. var likes = builder.NewCond()
  305. for _, v := range strings.Split(opts.Keyword, ",") {
  306. likes = likes.Or(builder.Like{"LOWER(tag)", strings.ToLower(v)})
  307. likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
  308. }
  309. keywordCond = keywordCond.Or(likes)
  310. cond = cond.And(keywordCond)
  311. }
  312. if len(opts.Topics) > 0 { //标签精确匹配
  313. var subQueryCond = builder.NewCond()
  314. for _, v := range strings.Split(opts.Keyword, ",") {
  315. subQueryCond = subQueryCond.Or(builder.Eq{"LOWER(image_topic.name)": strings.ToLower(v)})
  316. subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
  317. Join("INNER", "image_topic", "image_topic.id = image_topic_relation.topic_id").
  318. Where(subQueryCond).
  319. GroupBy("image_topic_relation.image_id")
  320. var topicCond = builder.In("id", subQuery)
  321. cond = cond.And(topicCond)
  322. }
  323. }
  324. if opts.IncludePublicOnly {
  325. cond = cond.And(builder.Eq{"is_private": false})
  326. }
  327. if opts.IncludePrivateOnly {
  328. cond = cond.And(builder.Eq{"is_private": true})
  329. }
  330. if opts.IncludeOwnerOnly {
  331. cond = cond.And(builder.Eq{"uid": opts.UID})
  332. }
  333. if opts.IncludeOfficialOnly {
  334. cond = cond.And(builder.Eq{"type": RECOMMOND_TYPE})
  335. }
  336. if opts.Status >= 0 {
  337. cond = cond.And(builder.Eq{"status": opts.Status})
  338. }
  339. if opts.IncludeStarByMe {
  340. subQuery := builder.Select("image_id").From("image_star").
  341. Where(builder.Eq{"uid": opts.UID})
  342. var starCond = builder.In("id", subQuery)
  343. cond = cond.And(starCond)
  344. }
  345. if opts.CloudbrainType > 0 {
  346. cond = cond.And(builder.Eq{"cloudbrain_type": opts.CloudbrainType})
  347. }
  348. return cond
  349. }
  350. func SearchImageByCondition(opts *SearchImageOptions, cond builder.Cond) (ImageList, int64, error) {
  351. if opts.Page <= 0 {
  352. opts.Page = 1
  353. }
  354. var err error
  355. sess := x.NewSession()
  356. defer sess.Close()
  357. images := make(ImageList, 0, opts.PageSize)
  358. count, err := sess.Where(cond).Count(new(Image))
  359. if err != nil {
  360. return nil, 0, fmt.Errorf("Count: %v", err)
  361. }
  362. sess.Where(cond).OrderBy(opts.SearchOrderBy.String())
  363. if opts.PageSize > 0 {
  364. sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
  365. }
  366. if err = sess.Find(&images); err != nil {
  367. return nil, 0, fmt.Errorf("Images: %v", err)
  368. }
  369. if err = images.loadAttributes(sess, opts.UID); err != nil {
  370. return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
  371. }
  372. return images, count, nil
  373. }
  374. func (images ImageList) loadAttributes(e Engine, uid int64) error {
  375. if len(images) == 0 {
  376. return nil
  377. }
  378. set := make(map[int64]struct{})
  379. for i := range images {
  380. set[images[i].UID] = struct{}{}
  381. }
  382. // Load creators.
  383. users := make(map[int64]*User, len(set))
  384. if err := e.Table("\"user\"").
  385. Cols("name", "lower_name", "avatar", "email").
  386. Where("id > 0").
  387. In("id", keysInt64(set)).
  388. Find(&users); err != nil {
  389. return fmt.Errorf("find users: %v", err)
  390. }
  391. for i := range images {
  392. if users[images[i].UID] != nil {
  393. images[i].UserName = users[images[i].UID].Name
  394. images[i].RelAvatarLink = users[images[i].UID].RelAvatarLink()
  395. } else {
  396. images[i].UserName = ""
  397. images[i].RelAvatarLink = ""
  398. }
  399. if uid == -1 {
  400. images[i].IsStar = false
  401. } else {
  402. images[i].IsStar = isImageStaring(e, uid, images[i].ID)
  403. }
  404. }
  405. return nil
  406. }
  407. func GetCommittingImageCount() int {
  408. total, err := x.Where("status =?", 0).Count(new(Image))
  409. if err != nil {
  410. return 0
  411. }
  412. return int(total)
  413. }
  414. func CreateLocalImage(image *Image) error {
  415. _, err := x.Insert(image)
  416. return err
  417. }
  418. func UpdateLocalImage(image *Image) error {
  419. _, err := x.ID(image.ID).Cols("description", "is_private", "status").Update(image)
  420. return err
  421. }
  422. func UpdateLocalImageStatus(image *Image) error {
  423. _, err := x.ID(image.ID).Cols("status").Update(image)
  424. return err
  425. }
  426. func UpdateAutoIncrementIndex() {
  427. x.Exec("SELECT setval('image_id_seq', (SELECT MAX(id) from image))")
  428. }
  429. func DeleteLocalImage(id int64) error {
  430. image := new(Image)
  431. _, err := x.ID(id).Delete(image)
  432. return err
  433. }
  434. //star or unstar Image
  435. func StarImage(userID, imageID int64, star bool) error {
  436. sess := x.NewSession()
  437. defer sess.Close()
  438. if err := sess.Begin(); err != nil {
  439. return err
  440. }
  441. if star {
  442. if isImageStaring(sess, userID, imageID) {
  443. return nil
  444. }
  445. if _, err := sess.Insert(&ImageStar{UID: userID, ImageID: imageID}); err != nil {
  446. return err
  447. }
  448. if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars + 1 WHERE id = ?", imageID); err != nil {
  449. return err
  450. }
  451. if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars + 1 WHERE id = ?", userID); err != nil {
  452. return err
  453. }
  454. } else {
  455. if !isImageStaring(sess, userID, imageID) {
  456. return nil
  457. }
  458. if _, err := sess.Delete(&ImageStar{0, userID, imageID, 0}); err != nil {
  459. return err
  460. }
  461. if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars - 1 WHERE id = ?", imageID); err != nil {
  462. return err
  463. }
  464. if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars - 1 WHERE id = ?", userID); err != nil {
  465. return err
  466. }
  467. }
  468. return sess.Commit()
  469. }
  470. func IsImageStaring(userID, datasetID int64) bool {
  471. return isImageStaring(x, userID, datasetID)
  472. }
  473. func isImageStaring(e Engine, userID, imageID int64) bool {
  474. has, _ := e.Get(&ImageStar{0, userID, imageID, 0})
  475. return has
  476. }
  477. func RecommendImage(imageId int64, recommond bool) error {
  478. image := Image{Type: GetRecommondType(recommond)}
  479. _, err := x.ID(imageId).Cols("type").Update(image)
  480. return err
  481. }
  482. func GetRecommondType(recommond bool) int {
  483. if recommond {
  484. return RECOMMOND_TYPE
  485. } else {
  486. return NORMAL_TYPE
  487. }
  488. }