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