##// END OF EJS Templates
Adde pull request voting recalculation
marcink -
r2481:4d303243 beta
parent child Browse files
Show More
@@ -26,6 +26,8 b' import logging'
26 import traceback
26 import traceback
27
27
28 from webob.exc import HTTPNotFound
28 from webob.exc import HTTPNotFound
29 from collections import defaultdict
30 from itertools import groupby
29
31
30 from pylons import request, response, session, tmpl_context as c, url
32 from pylons import request, response, session, tmpl_context as c, url
31 from pylons.controllers.util import abort, redirect
33 from pylons.controllers.util import abort, redirect
@@ -199,6 +201,24 b' class PullrequestsController(BaseRepoCon'
199 # valid ID
201 # valid ID
200 if not c.pull_request:
202 if not c.pull_request:
201 raise HTTPNotFound
203 raise HTTPNotFound
204 cc_model = ChangesetCommentsModel()
205 cs_model = ChangesetStatusModel()
206 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
207 pull_request=c.pull_request,
208 with_revisions=True)
209
210 cs_statuses = defaultdict(list)
211 for st in _cs_statuses:
212 cs_statuses[st.author.username] += [st]
213
214 c.pull_request_reviewers = []
215 for o in c.pull_request.reviewers:
216 st = cs_statuses.get(o.user.username, None)
217 if st:
218 sorter = lambda k: k.version
219 st = [(x, list(y)[0])
220 for x, y in (groupby(sorted(st, key=sorter), sorter))]
221 c.pull_request_reviewers.append([o.user, st])
202
222
203 # pull_requests repo_name we opened it against
223 # pull_requests repo_name we opened it against
204 # ie. other_repo must match
224 # ie. other_repo must match
@@ -210,23 +230,23 b' class PullrequestsController(BaseRepoCon'
210
230
211 # inline comments
231 # inline comments
212 c.inline_cnt = 0
232 c.inline_cnt = 0
213 c.inline_comments = ChangesetCommentsModel()\
233 c.inline_comments = cc_model.get_inline_comments(
214 .get_inline_comments(c.rhodecode_db_repo.repo_id,
234 c.rhodecode_db_repo.repo_id,
215 pull_request=pull_request_id)
235 pull_request=pull_request_id)
216 # count inline comments
236 # count inline comments
217 for __, lines in c.inline_comments:
237 for __, lines in c.inline_comments:
218 for comments in lines.values():
238 for comments in lines.values():
219 c.inline_cnt += len(comments)
239 c.inline_cnt += len(comments)
220 # comments
240 # comments
221 c.comments = ChangesetCommentsModel()\
241 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
222 .get_comments(c.rhodecode_db_repo.repo_id,
242 pull_request=pull_request_id)
223 pull_request=pull_request_id)
224
243
225 # changeset(pull-request) status
244 # changeset(pull-request) status
226 c.current_changeset_status = ChangesetStatusModel()\
245 c.current_changeset_status = cs_model.calculate_status(
227 .get_status(c.pull_request.org_repo,
246 c.pull_request_reviewers
228 pull_request=c.pull_request)
247 )
229 c.changeset_statuses = ChangesetStatus.STATUSES
248 c.changeset_statuses = ChangesetStatus.STATUSES
249
230 return render('/pullrequests/pullrequest_show.html')
250 return render('/pullrequests/pullrequest_show.html')
231
251
232 @jsonify
252 @jsonify
@@ -24,6 +24,7 b''
24
24
25
25
26 import logging
26 import logging
27 from collections import defaultdict
27
28
28 from rhodecode.model import BaseModel
29 from rhodecode.model import BaseModel
29 from rhodecode.model.db import ChangesetStatus, PullRequest
30 from rhodecode.model.db import ChangesetStatus, PullRequest
@@ -39,6 +40,52 b' class ChangesetStatusModel(BaseModel):'
39 def __get_pull_request(self, pull_request):
40 def __get_pull_request(self, pull_request):
40 return self._get_instance(PullRequest, pull_request)
41 return self._get_instance(PullRequest, pull_request)
41
42
43 def _get_status_query(self, repo, revision, pull_request,
44 with_revisions=False):
45 repo = self._get_repo(repo)
46
47 q = ChangesetStatus.query()\
48 .filter(ChangesetStatus.repo == repo)
49 if not with_revisions:
50 q = q.filter(ChangesetStatus.version == 0)
51
52 if revision:
53 q = q.filter(ChangesetStatus.revision == revision)
54 elif pull_request:
55 pull_request = self.__get_pull_request(pull_request)
56 q = q.filter(ChangesetStatus.pull_request == pull_request)
57 else:
58 raise Exception('Please specify revision or pull_request')
59 q.order_by(ChangesetStatus.version.asc())
60 return q
61
62 def calculate_status(self, statuses_by_reviewers):
63 """
64 leading one wins, if number of occurences are equal than weaker wins
65
66 :param statuses_by_reviewers:
67 """
68 status = None
69 votes = defaultdict(int)
70 reviewers_number = len(statuses_by_reviewers)
71 for user, statuses in statuses_by_reviewers:
72 if statuses:
73 ver, latest = statuses[0]
74 votes[latest.status] += 1
75 else:
76 votes[ChangesetStatus.DEFAULT] += 1
77
78 if votes.get(ChangesetStatus.STATUS_APPROVED) == reviewers_number:
79 return ChangesetStatus.STATUS_APPROVED
80 else:
81 return ChangesetStatus.STATUS_UNDER_REVIEW
82
83 def get_statuses(self, repo, revision=None, pull_request=None,
84 with_revisions=False):
85 q = self._get_status_query(repo, revision, pull_request,
86 with_revisions)
87 return q.all()
88
42 def get_status(self, repo, revision=None, pull_request=None):
89 def get_status(self, repo, revision=None, pull_request=None):
43 """
90 """
44 Returns latest status of changeset for given revision or for given
91 Returns latest status of changeset for given revision or for given
@@ -52,19 +99,7 b' class ChangesetStatusModel(BaseModel):'
52 :param pull_request: pull_request reference
99 :param pull_request: pull_request reference
53 :type:
100 :type:
54 """
101 """
55 repo = self._get_repo(repo)
102 q = self._get_status_query(repo, revision, pull_request)
56
57 q = ChangesetStatus.query()\
58 .filter(ChangesetStatus.repo == repo)\
59 .filter(ChangesetStatus.version == 0)
60
61 if revision:
62 q = q.filter(ChangesetStatus.revision == revision)
63 elif pull_request:
64 pull_request = self.__get_pull_request(pull_request)
65 q = q.filter(ChangesetStatus.pull_request == pull_request)
66 else:
67 raise Exception('Please specify revision or pull_request')
68
103
69 # need to use first here since there can be multiple statuses
104 # need to use first here since there can be multiple statuses
70 # returned from pull_request
105 # returned from pull_request
@@ -1371,14 +1371,17 b' class ChangesetStatus(Base, BaseModel):'
1371 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1371 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1372 'mysql_charset': 'utf8'}
1372 'mysql_charset': 'utf8'}
1373 )
1373 )
1374 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1375 STATUS_APPROVED = 'approved'
1376 STATUS_REJECTED = 'rejected'
1377 STATUS_UNDER_REVIEW = 'under_review'
1374
1378
1375 STATUSES = [
1379 STATUSES = [
1376 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1380 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1377 ('approved', _("Approved")),
1381 (STATUS_APPROVED, _("Approved")),
1378 ('rejected', _("Rejected")),
1382 (STATUS_REJECTED, _("Rejected")),
1379 ('under_review', _("Under Review")),
1383 (STATUS_UNDER_REVIEW, _("Under Review")),
1380 ]
1384 ]
1381 DEFAULT = STATUSES[0][0]
1382
1385
1383 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1386 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1384 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1387 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
@@ -1395,6 +1398,12 b' class ChangesetStatus(Base, BaseModel):'
1395 comment = relationship('ChangesetComment', lazy='joined')
1398 comment = relationship('ChangesetComment', lazy='joined')
1396 pull_request = relationship('PullRequest', lazy='joined')
1399 pull_request = relationship('PullRequest', lazy='joined')
1397
1400
1401 def __unicode__(self):
1402 return u"<%s('%s:%s')>" % (
1403 self.__class__.__name__,
1404 self.status, self.author
1405 )
1406
1398 @classmethod
1407 @classmethod
1399 def get_status_lbl(cls, value):
1408 def get_status_lbl(cls, value):
1400 return dict(cls.STATUSES).get(value)
1409 return dict(cls.STATUSES).get(value)
@@ -21,38 +21,62 b''
21 </div>
21 </div>
22
22
23 <h3>${_('Title')}: ${c.pull_request.title}</h3>
23 <h3>${_('Title')}: ${c.pull_request.title}</h3>
24 <div class="changeset-status-container" style="float:left;padding:0px 20px 20px 20px">
24 <div class="changeset-status-container" style="float:left;padding:0px 20px 0px 20px">
25 %if c.current_changeset_status:
25 %if c.current_changeset_status:
26 <div title="${_('Changeset status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
26 <div title="${_('Changeset status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
27 <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
27 <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
28 %endif
28 %endif
29 </div>
29 </div>
30 <div style="padding:4px">
30 <div style="padding:4px 4px 10px 4px">
31 <div>${h.fmt_date(c.pull_request.created_on)}</div>
31 <div>${h.fmt_date(c.pull_request.created_on)}</div>
32 </div>
32 </div>
33
33
34 ##DIFF
34 ## REVIEWERS
35
35 <div>
36 <div class="table">
36 <div class="table" style="float:right;width:46%;clear:none">
37 <div id="body" class="diffblock">
37 <div id="body" class="diffblock">
38 <div style="white-space:pre-wrap;padding:5px">${h.literal(c.pull_request.description)}</div>
38 <div style="white-space:pre-wrap;padding:5px">${_('Pull request reviewers')}</div>
39 </div>
39 </div>
40 <div id="changeset_compare_view_content">
40 <div style="border: 1px solid #CCC">
41 ##CS
41 <div class="group_members_wrap">
42 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
42 <ul class="group_members">
43 <%include file="/compare/compare_cs.html" />
43 %for user,status in c.pull_request_reviewers:
44
44 <li>
45 ## FILES
45 <div class="group_member">
46 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
46 <div style="float:left;padding:3px">
47 <div class="cs_files">
47 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
48 %for fid, change, f, stat in c.files:
48 </div>
49 <div class="cs_${change}">
49 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,20)}"/> </div>
50 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
50 <div style="float:left">${user.username}</div>
51 <div class="changes">${h.fancy_file_stats(stat)}</div>
52 </div>
51 </div>
52 </li>
53 %endfor
53 %endfor
54 </div>
54 </ul>
55 </div>
55 </div>
56 </div>
57 </div>
58 ##DIFF
59 <div class="table" style="float:left;width:46%;clear:none">
60 <div id="body" class="diffblock">
61 <div style="white-space:pre-wrap;padding:5px">${h.literal(c.pull_request.description)}</div>
62 </div>
63 <div id="changeset_compare_view_content">
64 ##CS
65 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
66 <%include file="/compare/compare_cs.html" />
67
68 ## FILES
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
70 <div class="cs_files">
71 %for fid, change, f, stat in c.files:
72 <div class="cs_${change}">
73 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
74 <div class="changes">${h.fancy_file_stats(stat)}</div>
75 </div>
76 %endfor
77 </div>
78 </div>
79 </div>
56 </div>
80 </div>
57 <script>
81 <script>
58 var _USERS_AC_DATA = ${c.users_array|n};
82 var _USERS_AC_DATA = ${c.users_array|n};
General Comments 0
You need to be logged in to leave comments. Login now