// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package models import ( "code.gitea.io/gitea/modules/setting" "fmt" "strings" //"code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/timeutil" //"fmt" "xorm.io/xorm" ) // A Poll on with the issues of a repository as candidates. type Poll struct { ID int64 `xorm:"pk autoincr"` RepoID int64 `xorm:"INDEX"` Repo *Repository `xorm:"-"` AuthorID int64 `xorm:"INDEX"` Author *User `xorm:"-"` //Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository. // When the poll is applied to all the issues, the subject should be an issue's trait. // eg: Quality, Importance, Urgency, Wholeness, Relevanceโ€ฆ Subject string `xorm:"name"` // Description may be used to describe at length the constitutive details of that poll. // Eg: Rationale, Deliberation Consequences, Schedule, Modus Operandiโ€ฆ // It can be written in the usual gitea-flavored markdown. Description string `xorm:"TEXT"` RenderedDescription string `xorm:"-"` Ref string // Do we need this? Are we even using it? WHat is it? Gradation string `xorm:"-"` AreCandidatesIssues bool // unused DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` ClosedUnix timeutil.TimeStamp `xorm:"INDEX"` // No idea how xorm works -- help! //Judgments []*PollJudgment `xorm:"-"` //Judgments JudgmentList `xorm:"-"` } // PollList is a list of polls offering additional functionality (perhaps) type PollList []*Poll func (poll *Poll) GetGradationList() []string { list := make([]string, 0, 6) // Placeholder until user customization somehow (poll.Gradation?) // - ๐Ÿคฎ๐Ÿ˜’๐Ÿ˜๐Ÿ™‚๐Ÿ˜€๐Ÿคฉ // - ๐Ÿ˜ซ๐Ÿ˜’๐Ÿ˜๐Ÿ˜Œ๐Ÿ˜€๐Ÿ˜ (more support, apparently) // - โ€ฆ list = append(list, "๐Ÿ˜ซ") list = append(list, "๐Ÿ˜’") list = append(list, "๐Ÿ˜") list = append(list, "๐Ÿ˜Œ") list = append(list, "๐Ÿ˜€") list = append(list, "๐Ÿ˜") return list } func (poll *Poll) GetGradeColorWord(grade uint8) (_ string) { // Another placeholder, bypassing the dragon for now switch grade { case 0: return "red" case 1: return "red" case 2: return "orange" case 3: return "yellow" case 4: return "olive" case 5: return "green" default: return "green" } } func (poll *Poll) GetGradeColorCode(grade uint8) (_ string) { // Another placeholder, bypassing the dragon for now switch grade { case 0: return "#E0361C" case 1: return "#EE6E00" case 2: return "#FDB200" case 3: return "#C6D700" case 4: return "#7EC239" case 5: return "#02AB58" case 6: return "#007E3D" default: return "#007E3D" } } func (poll *Poll) GetCandidatesIDs() (_ []int64, err error) { ids := make([]int64, 0, 10) if err := x.Table("poll_judgment"). Select("DISTINCT `poll_judgment`.`candidate_id`"). Where("`poll_judgment`.`poll_id` = ?", poll.ID). OrderBy("`poll_judgment`.`candidate_id` ASC"). Find(&ids); err != nil { return nil, err } return ids, nil } func (poll *Poll) GetJudgmentOnCandidate(judge *User, candidateID int64) (judgmernt *PollJudgment) { judgment, err := getJudgmentOfJudgeOnPollCandidate(x, judge.ID, poll.ID, candidateID) if nil != err { return nil } return judgment } func (poll *Poll) GetResult() (results *PollResult) { // The deliberator should probably be a parameter of this function, // and upstream we could fetch it from context or settings. deliberator := &PollNaiveDeliberator{ UseHighMean: false, } results, err := deliberator.Deliberate(poll) if nil != err { return nil // What should we do here? } return results } func (poll *Poll) CountGrades(candidateID int64, grade uint8) (_ uint64, err error) { rows := make([]int64, 0, 2) if err := x.Table("poll_judgment"). Select("COUNT(*) as amount"). Where("`poll_judgment`.`poll_id` = ?", poll.ID). And("`poll_judgment`.`candidate_id` = ?", candidateID). And("`poll_judgment`.`grade` = ?", grade). // Use Get() perhaps? Find(&rows); err != nil { return 0, err } if 1 != len(rows) { return 0, fmt.Errorf("wrong amount of COUNT()") } amount := uint64(rows[0]) return amount, nil } // $ figlet -w 120 "Create" // ____ _ // / ___|_ __ ___ __ _| |_ ___ // | | | '__/ _ \/ _` | __/ _ \ // | |___| | | __/ (_| | || __/ // \____|_| \___|\__,_|\__\___| // type CreatePollOptions struct { //Type PollType // for inline polls with their own candidates? Author *User Repo *Repository Subject string Description string //Grades string } func CreatePoll(opts *CreatePollOptions) (poll *Poll, err error) { sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return nil, err } poll, err = createPoll(sess, opts) if err != nil { return nil, err } if err = sess.Commit(); err != nil { return nil, err } return poll, nil } func createPoll(e *xorm.Session, opts *CreatePollOptions) (_ *Poll, err error) { poll := &Poll{ AuthorID: opts.Author.ID, Author: opts.Author, RepoID: opts.Repo.ID, Repo: opts.Repo, Subject: opts.Subject, Description: opts.Description, AreCandidatesIssues: true, } if _, err = e.Insert(poll); err != nil { return nil, err } if err = opts.Repo.getOwner(e); err != nil { return nil, err } //if err = updatePollInfos(e, opts, poll); err != nil { // return nil, err //} return poll, nil } // ____ _ // | _ \ ___ __ _ __| | // | |_) / _ \/ _` |/ _` | // | _ < __/ (_| | (_| | // |_| \_\___|\__,_|\__,_| // // GetPolls returns the (paginated) list of polls of a given repository and status. func GetPolls(repoID int64, page int) (PollList, error) { polls := make([]*Poll, 0, setting.UI.IssuePagingNum) sess := x.Where("repo_id = ?", repoID) //sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed) if page > 0 { sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum) } return polls, sess.Find(&polls) } func getPollByRepoID(e Engine, repoID, id int64) (*Poll, error) { m := new(Poll) has, err := e.ID(id).Where("repo_id = ?", repoID).Get(m) if err != nil { return nil, err } else if !has { return nil, ErrPollNotFound{ID: id, RepoID: repoID} } return m, nil } // GetPollByRepoID returns the poll in a repository. func GetPollByRepoID(repoID, id int64) (*Poll, error) { return getPollByRepoID(x, repoID, id) } // _ _ _ _ // | | | |_ __ __| | __ _| |_ ___ // | | | | '_ \ / _` |/ _` | __/ _ \ // | |_| | |_) | (_| | (_| | || __/ // \___/| .__/ \__,_|\__,_|\__\___| // |_| func updatePoll(e Engine, m *Poll) error { m.Subject = strings.TrimSpace(m.Subject) _, err := e.ID(m.ID).AllCols(). // Do some extra work here, like updating stats? //SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where( // builder.Eq{ // "poll_id": m.ID, // "is_closed": true, // }, //)). Update(m) return err } // UpdatePoll updates information of given poll. func UpdatePoll(m *Poll) error { sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { return err } if err := updatePoll(sess, m); err != nil { return err } //if err := updatePollCompleteness(sess, m.ID); err != nil { // return err //} return sess.Commit() } // ____ _ _ // | _ \ ___| | ___| |_ ___ // | | | |/ _ \ |/ _ \ __/ _ \ // | |_| | __/ | __/ || __/ // |____/ \___|_|\___|\__\___| // // DeletePollByRepoID deletes a poll from a repository. func DeletePollByRepoID(repoID, id int64) error { m, err := GetPollByRepoID(repoID, id) if err != nil { if IsErrPollNotFound(err) { return nil // not very confident in this ; yelling is best? } return err } sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err } if _, err = sess.ID(m.ID).Delete(new(Poll)); err != nil { return err } return sess.Commit() }