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