|
|
|
@ -6,11 +6,14 @@ package models
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
|
|
|
issues_model "code.gitea.io/gitea/models/issues"
|
|
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
|
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
|
|
|
"code.gitea.io/gitea/modules/setting"
|
|
|
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
|
|
|
"fmt"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
@ -22,17 +25,31 @@ type Poll struct {
|
|
|
|
|
AuthorID int64 `xorm:"INDEX"`
|
|
|
|
|
Author *user_model.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…
|
|
|
|
|
|
|
|
|
|
// The Subject of the poll should be short
|
|
|
|
|
// When the poll uses issues as candidates, the subject should be an issue's trait.
|
|
|
|
|
// eg: Quality, Importance, Urgency, Wholeness, Relevance, Enthusiasm…
|
|
|
|
|
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:"-"`
|
|
|
|
|
Ref string // Do we need this? Are we even using it? What is it?
|
|
|
|
|
|
|
|
|
|
Gradation string `xorm:"-"`
|
|
|
|
|
|
|
|
|
|
// Examples of a Candidates Whitelist
|
|
|
|
|
// 15
|
|
|
|
|
// 1, 12, #13
|
|
|
|
|
// Ideas:
|
|
|
|
|
// 3-13, 15-99
|
|
|
|
|
// tag=bug, tag=bugfix, 21878
|
|
|
|
|
// or perhaps
|
|
|
|
|
// [bug], [bugfix], 21878
|
|
|
|
|
// but ideally an issue _search bar_ command
|
|
|
|
|
CandidatesWhitelist string `xorm:"candidates_whitelist"`
|
|
|
|
|
AreCandidatesIssues bool // unused -- is this even the way to go ?
|
|
|
|
|
|
|
|
|
|
DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"`
|
|
|
|
@ -45,6 +62,10 @@ type Poll struct {
|
|
|
|
|
//Judgments JudgmentList `xorm:"-"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
db.RegisterModel(new(Poll))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PollList is a list of polls offering additional functionality (perhaps)
|
|
|
|
|
type PollList []*Poll
|
|
|
|
|
|
|
|
|
@ -109,6 +130,7 @@ func (poll *Poll) GetGradeColorCode(grade uint8) (_ string) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetCandidatesIDs collects the IDs of Candidates having received at least one judgment.
|
|
|
|
|
func (poll *Poll) GetCandidatesIDs() (_ []int64, err error) {
|
|
|
|
|
ids := make([]int64, 0, 10)
|
|
|
|
|
x := db.GetEngine(db.DefaultContext)
|
|
|
|
@ -122,6 +144,40 @@ func (poll *Poll) GetCandidatesIDs() (_ []int64, err error) {
|
|
|
|
|
return ids, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AllowsIssueAsCandidate checks if this Poll may use the provided Issue as Candidate
|
|
|
|
|
func (poll *Poll) AllowsIssueAsCandidate(issue *issues_model.Issue) bool {
|
|
|
|
|
|
|
|
|
|
if poll.CandidatesWhitelist == "" {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Idea: Instead of handling our parsing here, perhaps use the search bar utils ?
|
|
|
|
|
csvSeperator := regexp.MustCompile(`\s*,\s*`)
|
|
|
|
|
issueIdRegex := regexp.MustCompile(`#?(?P<id>[0-9]+)`)
|
|
|
|
|
issueIdIndex := issueIdRegex.SubexpIndex("id")
|
|
|
|
|
|
|
|
|
|
candidatesFilters := csvSeperator.Split(poll.CandidatesWhitelist, -1)
|
|
|
|
|
for _, candidateFilter := range candidatesFilters {
|
|
|
|
|
|
|
|
|
|
issueIdMatches := issueIdRegex.FindStringSubmatch(candidateFilter)
|
|
|
|
|
if nil != issueIdMatches {
|
|
|
|
|
acceptedIssueID, err := strconv.Atoi(issueIdMatches[issueIdIndex])
|
|
|
|
|
if nil == err {
|
|
|
|
|
if issue.Index == int64(acceptedIssueID) {
|
|
|
|
|
fmt.Println("YEP")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// … perhaps match tags as well, and other filters ? → search bar !?!
|
|
|
|
|
// /!. CURRENT IMPLEMENTATION ABOVE WILL CHOKE ON COMMAS IN TAG NAMES
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Println("NOPE")
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (poll *Poll) GetJudgmentOnCandidate(judge *user_model.User, candidateID int64) (judgment *PollJudgment) {
|
|
|
|
|
x := db.GetEngine(db.DefaultContext)
|
|
|
|
|
judgment, err := getJudgmentOfJudgeOnPollCandidate(x, judge.ID, poll.ID, candidateID)
|
|
|
|
@ -177,10 +233,11 @@ func (poll *Poll) CountGrades(candidateID int64, grade uint8) (_ uint64, err err
|
|
|
|
|
|
|
|
|
|
type CreatePollOptions struct {
|
|
|
|
|
//Type PollType // for inline polls with their own candidates?
|
|
|
|
|
Author *user_model.User
|
|
|
|
|
Repo *repo_model.Repository
|
|
|
|
|
Subject string
|
|
|
|
|
Description string
|
|
|
|
|
Author *user_model.User
|
|
|
|
|
Repo *repo_model.Repository
|
|
|
|
|
Subject string
|
|
|
|
|
Description string
|
|
|
|
|
CandidatesWhitelist string
|
|
|
|
|
//Grades string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -213,6 +270,7 @@ func createPoll(e db.Engine, opts *CreatePollOptions) (_ *Poll, err error) {
|
|
|
|
|
Repo: opts.Repo,
|
|
|
|
|
Subject: opts.Subject,
|
|
|
|
|
Description: opts.Description,
|
|
|
|
|
CandidatesWhitelist: opts.CandidatesWhitelist,
|
|
|
|
|
AreCandidatesIssues: true,
|
|
|
|
|
}
|
|
|
|
|
if _, err = e.Insert(poll); err != nil {
|
|
|
|
@ -246,6 +304,8 @@ func GetPolls(repoID int64, page int) (PollList, error) {
|
|
|
|
|
//sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
|
|
|
|
|
if page > 0 {
|
|
|
|
|
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
|
|
|
|
|
} else {
|
|
|
|
|
sess = sess.Limit(setting.UI.IssuePagingNum)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return polls, sess.Find(&polls)
|
|
|
|
@ -264,8 +324,31 @@ func getPollByRepoID(e db.Engine, repoID, id int64) (*Poll, error) {
|
|
|
|
|
|
|
|
|
|
// GetPollByRepoID returns the poll in a repository.
|
|
|
|
|
func GetPollByRepoID(repoID, id int64) (*Poll, error) {
|
|
|
|
|
x := db.GetEngine(db.DefaultContext)
|
|
|
|
|
return getPollByRepoID(x, repoID, id)
|
|
|
|
|
return getPollByRepoID(db.GetEngine(db.DefaultContext), repoID, id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetPollsOnIssue returns the (paginated) list of polls active on a given issue.
|
|
|
|
|
func GetPollsOnIssue(issue *issues_model.Issue) (PollList, error) {
|
|
|
|
|
polls := make([]*Poll, 0, setting.UI.IssuePagingNum)
|
|
|
|
|
e := db.GetEngine(db.DefaultContext)
|
|
|
|
|
sess := e.Where("repo_id = ?", issue.RepoID)
|
|
|
|
|
//sess := e.Where("repo_id = ? AND is_closed = ?", issue, isClosed)
|
|
|
|
|
// TODO: exclude closed polls
|
|
|
|
|
// TODO: sort by decreasing priority, and then by decreasing creation date
|
|
|
|
|
|
|
|
|
|
err := sess.Find(&polls)
|
|
|
|
|
if nil != err {
|
|
|
|
|
return polls, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filteredPolls := make([]*Poll, 0, setting.UI.IssuePagingNum)
|
|
|
|
|
for _, poll := range polls {
|
|
|
|
|
if poll.AllowsIssueAsCandidate(issue) {
|
|
|
|
|
filteredPolls = append(filteredPolls, poll)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filteredPolls, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// _ _ _ _
|
|
|
|
@ -326,6 +409,8 @@ func DeletePollByRepoID(repoID, id int64) error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME: check if user is allowed to do this
|
|
|
|
|
|
|
|
|
|
ctx, committer, err := db.TxContext()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|