예제 #1
0
// summarizeIssue fetches a single codereview issue and all its
// comments and summarizes its state by reading the comments.
func summarizeIssue(env task.Env, id int) (im issueMeta) {
	im.issue = id
	im.policyVersion = policyVersion
	url := urlWithParams(messagesQuery, map[string]string{
		"ISSUE_NUMBER": strconv.Itoa(id),
	})
	c := env.HTTPClient()
	res, err := c.Get(url)
	env.Logf("Fetching %s = %v", url, err)
	if err != nil {
		im.err = err
		return
	}
	defer res.Body.Close()
	var r issueResult
	err = json.NewDecoder(res.Body).Decode(&r)
	if err != nil {
		im.err = err
		return
	}
	im.lastModified = r.Modified

	if r.Closed {
		im.reviewer = "close"
		return
	}
	open := true
	var lastMsg *message
	sort.Sort(byMessageDate(r.Messages))
	for _, m := range r.Messages {
		lastMsg = m
		if m := reviewRx.FindStringSubmatch(m.Text); m != nil {
			if m[1] == "close" || m[1] == "closed" {
				open = false
				continue
			}
			open = true
			im.reviewer = m[1]
			continue
		}
		if strings.HasPrefix(m.Text, "PTAL") {
			open = true
		}
	}
	if lastMsg != nil {
		if m := qRx.FindStringSubmatch(lastMsg.Text); m != nil {
			cmd := m[1]
			if cmd == "wait" {
				open = false
			}
		}
		if strings.Contains(lastMsg.Text, "*** Submitted as http://code.google.com/p/go/source/") {
			open = false
		}
	}
	if !open {
		im.reviewer = "close"
	}
	return
}
예제 #2
0
func (codereviewTask) Poll(env task.Env) (pts []*task.PolledTask, err error) {
	type res struct {
		pt  []*task.PolledTask
		cc  bool
		err error
	}
	var chs []chan res
	for _, cc := range []bool{false, true} {
		for _, month := range relevantMonths() {
			ch := make(chan res, 1)
			chs = append(chs, ch)
			go func(month string, cc bool) {
				pt, err := pollMonth(env, month, cc)
				env.Logf("Month %q (cc=%v) = %d, %v", month, cc, len(pt), err)
				ch <- res{pt, cc, err}
			}(month, cc)
		}
	}
	seen := make(map[string]bool) // ID -> true
	for _, ch := range chs {
		res := <-ch
		if res.err != nil {
			return nil, res.err
		}
		for _, pt := range res.pt {
			if !seen[pt.ID] {
				seen[pt.ID] = true
				pts = append(pts, pt)
			}
		}
	}
	return pts, nil
}
예제 #3
0
파일: issue.go 프로젝트: bradfitz/qopher
func (issueTask) Poll(env task.Env) ([]*task.PolledTask, error) {
	c := env.HTTPClient()
	res, err := c.Get(query)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	issues, err := ParseIssues(res.Body)
	if err != nil {
		return nil, err
	}
	env.Logf("got %d issues", len(issues))
	tasks := make([]*task.PolledTask, 0, len(issues))
	for _, issue := range issues {
		if issue.Owner != nil {
			// Owned issues aren't logically open
			continue
		}
		t := &task.PolledTask{
			Title: issue.Title,
		}
		if m := idFromURL.FindStringSubmatch(issue.ID); m != nil {
			t.ID = m[1]
		} else {
			env.Logf("Bogus ID %q", issue.ID)
			continue
		}
		tasks = append(tasks, t)
	}
	env.Logf("returning %d tasks", len(tasks))
	return tasks, nil
}
예제 #4
0
func pollMonth(env task.Env, month string, cc bool) (pt []*task.PolledTask, err error) {
	c := env.HTTPClient()
	cursor := ""
	n := 0
	var (
		metawg  sync.WaitGroup // for meta or metaerr to be loaded
		meta    monthMeta
		metaerr error
	)

	metawg.Add(1)
	go func() {
		defer metawg.Done()
		meta, metaerr = getMonthMeta(env, month)
	}()

	// Opens issue to rietveld, even if they're logically closed
	// (R=closed) to us.
	var rietOpen []*Review

	for {
		query := monthQuery
		if cc {
			query = monthQueryCC
		}
		url := urlWithParams(query, map[string]string{
			"DATE_AFTER":  month + "-01 00:00:00",
			"DATE_BEFORE": monthAfter(month) + "-01 00:00:00",
			"CURSOR":      cursor,
			"LIMIT":       fmt.Sprint(itemsPerPage),
		})
		n++
		env.Logf("Fetching codereview page %d: %s", n, url)
		res, err := c.Get(url)
		if err != nil {
			env.Logf("Error fetching %s: %v", url, err)
			return nil, err
		}
		var reviews []*Review
		reviews, cursor, err = ParseReviews(res.Body)
		res.Body.Close()
		if err != nil {
			env.Logf("ParseReviews error: %v", err)
			return nil, err
		}
		env.Logf("got %d reviews, cursor is %q", len(reviews), cursor)
		for _, r := range reviews {
			if r.Issue == 0 {
				env.Logf("bogus issue %+v", r)
				continue
			}
			rietOpen = append(rietOpen, r)
		}
		if cursor == "" || len(reviews) < itemsPerPage {
			break
		}
	}

	metawg.Wait()
	if metaerr != nil {
		env.Logf("Error loading month meta for %q: %v", month, metaerr)
		return nil, metaerr
	}

	// For each issue that Rietveld believes to be open, see if we
	// have the latest comments for it and then see if it's
	// actually logically still open, based on its comments.
	var updates []chan issueMeta
	for _, r := range rietOpen {
		im := meta[r.Issue]
		if im.policyVersion != policyVersion || im.lastModified != r.Modified {
			env.Logf("Need to summarize issue %d", r.Issue)
			ch := make(chan issueMeta, 1)
			updates = append(updates, ch)
			go func(issueId int) {
				ch <- summarizeIssue(env, issueId)
			}(r.Issue)
		} else {
			env.Logf("Issue %d is unmodified.", r.Issue)
		}
	}
	var updateErr error
	var errCount int
	for _, ch := range updates {
		im := <-ch
		if im.err != nil {
			errCount++
			if updateErr == nil {
				updateErr = im.err
			}
		} else {
			meta[im.issue] = im
		}
	}
	if updateErr != nil {
		// Save what we've got so far.
		env.Logf("Errors re-fetching month %q: %d/%d fetches failed. Saving %d good issue summaries.", month, errCount, len(updates), len(meta))
		env.SetMeta(monthMetaPrefix+month, meta.serialize())
		return nil, updateErr
	}
	if len(updates) > 0 {
		if err := env.SetMeta(monthMetaPrefix+month, meta.serialize()); err != nil {
			env.Logf("Error writing month meta for %q: %v", month, err)
			return nil, err
		}
	}
	for _, r := range rietOpen {
		im := meta[r.Issue]
		if im.reviewer == "close" {
			continue
		}
		ownerHint := im.reviewer
		if ownerHint == "" {
			ownerHint = r.Reviewer()
		}
		t := &task.PolledTask{
			ID:        fmt.Sprint(r.Issue),
			Title:     r.Desc,
			OwnerHint: ownerHint,
		}
		pt = append(pt, t)
	}

	env.Logf("codereview: for month %q, returning %d open tasks", month, len(pt))
	return pt, nil
}