package models import ( "fmt" "strings" "xorm.io/builder" "code.gitea.io/gitea/modules/timeutil" ) const RECOMMOND_TYPE = 5 const NORMAL_TYPE = 0 type Image struct { ID int64 `xorm:"pk autoincr"` Type int `xorm:"INDEX NOT NULL"` //0 normal 5官方推荐,中间值保留为后续扩展 CloudbrainType int `xorm:"INDEX NOT NULL"` //0 云脑一 1云脑二 UID int64 `xorm:"INDEX NOT NULL"` IsPrivate bool `xorm:"INDEX NOT NULL"` Tag string `xorm:"varchar(100) UNIQUE"` Description string `xorm:"varchar(765)"` Topics []string `xorm:"TEXT JSON"` Place string `xorm:"varchar(300)"` NumStars int `xorm:"NOT NULL DEFAULT 0"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } type ImageStar struct { ID int64 `xorm:"pk autoincr"` UID int64 `xorm:"UNIQUE(s)"` ImageID int64 `xorm:"UNIQUE(s)"` CreatedUnix timeutil.TimeStamp `xorm:"created"` } type ImageTopic struct { ID int64 Name string `xorm:"UNIQUE VARCHAR(105)"` ImageCount int CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } type ImageTopicRelation struct { ImageID int64 `xorm:"UNIQUE(s)"` TopicID int64 `xorm:"UNIQUE(s)"` } type SearchImageOptions struct { Keyword string UID int64 IncludePublic bool IncludeStarByMe bool IncludeCustom bool CodeLanguage string Framework string CudaVersion string ListOptions SearchOrderBy } type ErrorImageTagExist struct { Tag string } func (err ErrorImageTagExist) Error() string { return fmt.Sprintf("Image already exists [tag: %s]", err.Tag) } type ErrImageNotExist struct { ID int64 } func (err ErrImageNotExist) Error() string { return fmt.Sprintf("Image does not exist [id: %d]", err.ID) } func IsErrImageTagExist(err error) bool { _, ok := err.(ErrorImageTagExist) return ok } func IsImageExist(tag string) (bool, error) { return x.Exist(&Image{ Tag: tag, }) } type FindImageTopicOptions struct { ListOptions ImageID int64 Keyword string } func (opts *FindImageTopicOptions) toConds() builder.Cond { var cond = builder.NewCond() if opts.ImageID > 0 { cond = cond.And(builder.Eq{"image_topic.image_id": opts.ImageID}) } if opts.Keyword != "" { cond = cond.And(builder.Like{"image_topic.name", strings.ToLower(opts.Keyword)}) } return cond } func GetImageByID(id int64) (*Image, error) { rel := new(Image) has, err := x. ID(id). Get(rel) if err != nil { return nil, err } else if !has { return nil, ErrImageNotExist{id} } return rel, nil } func FindImageTopics(opts *FindImageTopicOptions) (topics []*ImageTopic, err error) { sess := x.Select("image_topic.*").Where(opts.toConds()) if opts.ImageID > 0 { sess.Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id") } if opts.PageSize != 0 && opts.Page != 0 { sess = opts.setSessionPagination(sess) } return topics, sess.Desc("image_topic.image_count").Find(&topics) } func SaveImageTopics(imageID int64, topicNames ...string) error { topics, err := FindImageTopics(&FindImageTopicOptions{ ImageID: imageID, }) if err != nil { return err } sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { return err } var addedTopicNames []string for _, topicName := range topicNames { if strings.TrimSpace(topicName) == "" { continue } var found bool for _, t := range topics { if strings.EqualFold(topicName, t.Name) { found = true break } } if !found { addedTopicNames = append(addedTopicNames, topicName) } } var removeTopics []*ImageTopic for _, t := range topics { var found bool for _, topicName := range topicNames { if strings.EqualFold(topicName, t.Name) { found = true break } } if !found { removeTopics = append(removeTopics, t) } } for _, topicName := range addedTopicNames { _, err := addTopicByNameToImage(sess, imageID, topicName) if err != nil { return err } } for _, topic := range removeTopics { err := removeTopicFromImage(sess, imageID, topic) if err != nil { return err } } topicNames = make([]string, 0, 25) if err := sess.Table("image_topic").Cols("name"). Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id"). Where("image_topic_relation.image_id = ?", imageID).Desc("image_topic.image_count").Find(&topicNames); err != nil { return err } if _, err := sess.ID(imageID).Cols("topics").Update(&Image{ Topics: topicNames, }); err != nil { return err } return sess.Commit() } func addTopicByNameToImage(e Engine, imageID int64, topicName string) (*ImageTopic, error) { var topic ImageTopic has, err := e.Where("name = ?", topicName).Get(&topic) if err != nil { return nil, err } if !has { topic.Name = topicName topic.ImageCount = 1 if _, err := e.Insert(&topic); err != nil { return nil, err } } else { topic.ImageCount++ if _, err := e.ID(topic.ID).Cols("image_count").Update(&topic); err != nil { return nil, err } } if _, err := e.Insert(&ImageTopicRelation{ ImageID: imageID, TopicID: topic.ID, }); err != nil { return nil, err } return &topic, nil } func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error { topic.ImageCount-- if _, err := e.ID(topic.ID).Cols("image_count").Update(topic); err != nil { return err } if _, err := e.Delete(&ImageTopicRelation{ ImageID: imageId, TopicID: topic.ID, }); err != nil { return err } return nil } /*func SearchImage(opts *SearchImageOptions) ([]*Image, int64, error) { cond := SearchImageCondition(opts) return SearchImageByCondition(opts, cond) }*/ func SearchImageCondition(opts *SearchImageOptions) builder.Cond { var cond = builder.NewCond() if len(opts.Keyword) > 0 { cond = cond.And(builder.Or(builder.Like{"image.tag", opts.Keyword}, builder.Like{"image.description", opts.Keyword})) } if len(opts.CudaVersion) > 0 { cond = cond.And(builder.Eq{"image.cuda_version": opts.CudaVersion}) } if len(opts.CodeLanguage) > 0 { cond = cond.And(builder.Eq{"image.code_language": opts.CodeLanguage}) } if len(opts.Framework) > 0 { cond = cond.And(builder.Eq{"image.framework": opts.Framework}) } if opts.IncludePublic { cond = cond.And(builder.Eq{"image.is_private": false}) } /** if opts.IncludeStarByMe { cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) cond = cond.And(builder.Eq{"attachment.is_private": false}) if opts.OwnerID > 0 { if len(opts.Keyword) == 0 { cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) } else { subCon := builder.NewCond() subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID}, builder.Or(builder.Like{"dataset.title", opts.Keyword}, builder.Like{"dataset.description", opts.Keyword})) cond = cond.Or(subCon) } } } else if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) if !opts.IsOwner { cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) cond = cond.And(builder.Eq{"attachment.is_private": false}) } }*/ return cond } /*func SearchImageByCondition(opts *SearchImageOptions, cond builder.Cond) ([]*Image, int64, error) { if opts.Page <= 0 { opts.Page = 1 } var err error sess := x.NewSession() defer sess.Close() datasets := make(DatasetList, 0, opts.PageSize) 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" count, err := sess.Distinct("dataset.id").Join("INNER", "repository", "repository.id = dataset.repo_id"). Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). Where(cond).Count(new(Dataset)) if err != nil { return nil, 0, fmt.Errorf("Count: %v", err) } sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). Where(cond).OrderBy(opts.SearchOrderBy.String()) if opts.PageSize > 0 { sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) } if err = sess.Find(&datasets); err != nil { return nil, 0, fmt.Errorf("Dataset: %v", err) } if err = datasets.loadAttributes(sess); err != nil { return nil, 0, fmt.Errorf("LoadAttributes: %v", err) } return datasets, count, nil }*/ func CreateLocalImage(image *Image) error { _, err := x.Insert(image) return err } //star or unstar Image func StarImage(userID, imageID int64, star bool) error { sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { return err } if star { if isImageStaring(sess, userID, imageID) { return nil } if _, err := sess.Insert(&ImageStar{UID: userID, ImageID: imageID}); err != nil { return err } if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars + 1 WHERE id = ?", imageID); err != nil { return err } if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars + 1 WHERE id = ?", userID); err != nil { return err } } else { if !isImageStaring(sess, userID, imageID) { return nil } if _, err := sess.Delete(&ImageStar{0, userID, imageID, 0}); err != nil { return err } if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars - 1 WHERE id = ?", imageID); err != nil { return err } if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars - 1 WHERE id = ?", userID); err != nil { return err } } return sess.Commit() } func IsImageStaring(userID, datasetID int64) bool { return isImageStaring(x, userID, datasetID) } func isImageStaring(e Engine, userID, imageID int64) bool { has, _ := e.Get(&ImageStar{0, userID, imageID, 0}) return has } func RecommendImage(imageId int64, recommond bool) error { image := Image{Type: getRecommondType(recommond)} _, err := x.ID(imageId).Cols("type").Update(image) return err } func getRecommondType(recommond bool) int { if recommond { return RECOMMOND_TYPE } else { return NORMAL_TYPE } }