changeset 2481:4d3032431d4f beta

Adde pull request voting recalculation
author Marcin Kuzminski <marcin@python-works.com>
date Tue, 19 Jun 2012 00:43:55 +0200
parents cb9e73b29a87
children 1fd3c043c025
files rhodecode/controllers/pullrequests.py rhodecode/model/changeset_status.py rhodecode/model/db.py rhodecode/templates/pullrequests/pullrequest_show.html
diffstat 4 files changed, 137 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/controllers/pullrequests.py	Mon Jun 18 21:31:03 2012 +0200
+++ b/rhodecode/controllers/pullrequests.py	Tue Jun 19 00:43:55 2012 +0200
@@ -26,6 +26,8 @@
 import traceback
 
 from webob.exc import HTTPNotFound
+from collections import defaultdict
+from itertools import groupby
 
 from pylons import request, response, session, tmpl_context as c, url
 from pylons.controllers.util import abort, redirect
@@ -199,6 +201,24 @@
         # valid ID
         if not c.pull_request:
             raise HTTPNotFound
+        cc_model = ChangesetCommentsModel()
+        cs_model = ChangesetStatusModel()
+        _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
+                                            pull_request=c.pull_request,
+                                            with_revisions=True)
+
+        cs_statuses = defaultdict(list)
+        for st in _cs_statuses:
+            cs_statuses[st.author.username] += [st]
+
+        c.pull_request_reviewers = []
+        for o in c.pull_request.reviewers:
+            st = cs_statuses.get(o.user.username, None)
+            if st:
+                sorter = lambda k: k.version
+                st = [(x, list(y)[0])
+                      for x, y in (groupby(sorted(st, key=sorter), sorter))]
+            c.pull_request_reviewers.append([o.user, st])
 
         # pull_requests repo_name we opened it against
         # ie. other_repo must match
@@ -210,23 +230,23 @@
 
         # inline comments
         c.inline_cnt = 0
-        c.inline_comments = ChangesetCommentsModel()\
-                            .get_inline_comments(c.rhodecode_db_repo.repo_id,
-                                                 pull_request=pull_request_id)
+        c.inline_comments = cc_model.get_inline_comments(
+                                c.rhodecode_db_repo.repo_id,
+                                pull_request=pull_request_id)
         # count inline comments
         for __, lines in c.inline_comments:
             for comments in lines.values():
                 c.inline_cnt += len(comments)
         # comments
-        c.comments = ChangesetCommentsModel()\
-                          .get_comments(c.rhodecode_db_repo.repo_id,
-                                        pull_request=pull_request_id)
+        c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
+                                           pull_request=pull_request_id)
 
         # changeset(pull-request) status
-        c.current_changeset_status = ChangesetStatusModel()\
-                              .get_status(c.pull_request.org_repo,
-                                          pull_request=c.pull_request)
+        c.current_changeset_status = cs_model.calculate_status(
+                                        c.pull_request_reviewers
+                                     )
         c.changeset_statuses = ChangesetStatus.STATUSES
+
         return render('/pullrequests/pullrequest_show.html')
 
     @jsonify
--- a/rhodecode/model/changeset_status.py	Mon Jun 18 21:31:03 2012 +0200
+++ b/rhodecode/model/changeset_status.py	Tue Jun 19 00:43:55 2012 +0200
@@ -24,6 +24,7 @@
 
 
 import logging
+from collections import  defaultdict
 
 from rhodecode.model import BaseModel
 from rhodecode.model.db import ChangesetStatus, PullRequest
@@ -39,6 +40,52 @@
     def __get_pull_request(self, pull_request):
         return self._get_instance(PullRequest, pull_request)
 
+    def _get_status_query(self, repo, revision, pull_request,
+                          with_revisions=False):
+        repo = self._get_repo(repo)
+
+        q = ChangesetStatus.query()\
+            .filter(ChangesetStatus.repo == repo)
+        if not with_revisions:
+            q = q.filter(ChangesetStatus.version == 0)
+
+        if revision:
+            q = q.filter(ChangesetStatus.revision == revision)
+        elif pull_request:
+            pull_request = self.__get_pull_request(pull_request)
+            q = q.filter(ChangesetStatus.pull_request == pull_request)
+        else:
+            raise Exception('Please specify revision or pull_request')
+        q.order_by(ChangesetStatus.version.asc())
+        return q
+
+    def calculate_status(self, statuses_by_reviewers):
+        """
+        leading one wins, if number of occurences are equal than weaker wins
+
+        :param statuses_by_reviewers:
+        """
+        status = None
+        votes = defaultdict(int)
+        reviewers_number = len(statuses_by_reviewers)
+        for user, statuses in statuses_by_reviewers:
+            if statuses:
+                ver, latest = statuses[0]
+                votes[latest.status] += 1
+            else:
+                votes[ChangesetStatus.DEFAULT] += 1
+
+        if votes.get(ChangesetStatus.STATUS_APPROVED) == reviewers_number:
+            return ChangesetStatus.STATUS_APPROVED
+        else:
+            return ChangesetStatus.STATUS_UNDER_REVIEW
+
+    def get_statuses(self, repo, revision=None, pull_request=None,
+                     with_revisions=False):
+        q = self._get_status_query(repo, revision, pull_request,
+                                   with_revisions)
+        return q.all()
+
     def get_status(self, repo, revision=None, pull_request=None):
         """
         Returns latest status of changeset for given revision or for given
@@ -52,19 +99,7 @@
         :param pull_request: pull_request reference
         :type:
         """
-        repo = self._get_repo(repo)
-
-        q = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == repo)\
-            .filter(ChangesetStatus.version == 0)
-
-        if revision:
-            q = q.filter(ChangesetStatus.revision == revision)
-        elif pull_request:
-            pull_request = self.__get_pull_request(pull_request)
-            q = q.filter(ChangesetStatus.pull_request == pull_request)
-        else:
-            raise Exception('Please specify revision or pull_request')
+        q = self._get_status_query(repo, revision, pull_request)
 
         # need to use first here since there can be multiple statuses
         # returned from pull_request
--- a/rhodecode/model/db.py	Mon Jun 18 21:31:03 2012 +0200
+++ b/rhodecode/model/db.py	Tue Jun 19 00:43:55 2012 +0200
@@ -1371,14 +1371,17 @@
         {'extend_existing': True, 'mysql_engine': 'InnoDB',
          'mysql_charset': 'utf8'}
     )
+    STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
+    STATUS_APPROVED = 'approved'
+    STATUS_REJECTED = 'rejected'
+    STATUS_UNDER_REVIEW = 'under_review'
 
     STATUSES = [
-        ('not_reviewed', _("Not Reviewed")),  # (no icon) and default
-        ('approved', _("Approved")),
-        ('rejected', _("Rejected")),
-        ('under_review', _("Under Review")),
+        (STATUS_NOT_REVIEWED, _("Not Reviewed")),  # (no icon) and default
+        (STATUS_APPROVED, _("Approved")),
+        (STATUS_REJECTED, _("Rejected")),
+        (STATUS_UNDER_REVIEW, _("Under Review")),
     ]
-    DEFAULT = STATUSES[0][0]
 
     changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
     repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
@@ -1395,6 +1398,12 @@
     comment = relationship('ChangesetComment', lazy='joined')
     pull_request = relationship('PullRequest', lazy='joined')
 
+    def __unicode__(self):
+        return u"<%s('%s:%s')>" % (
+            self.__class__.__name__,
+            self.status, self.author
+        )
+
     @classmethod
     def get_status_lbl(cls, value):
         return dict(cls.STATUSES).get(value)
--- a/rhodecode/templates/pullrequests/pullrequest_show.html	Mon Jun 18 21:31:03 2012 +0200
+++ b/rhodecode/templates/pullrequests/pullrequest_show.html	Tue Jun 19 00:43:55 2012 +0200
@@ -21,38 +21,62 @@
     </div>
 
     <h3>${_('Title')}: ${c.pull_request.title}</h3>
-    <div class="changeset-status-container" style="float:left;padding:0px 20px 20px 20px">
+    <div class="changeset-status-container" style="float:left;padding:0px 20px 0px 20px">
         %if c.current_changeset_status:
           <div title="${_('Changeset status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
           <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
         %endif
     </div>
-    <div style="padding:4px">
+    <div style="padding:4px 4px 10px 4px">
       <div>${h.fmt_date(c.pull_request.created_on)}</div>
     </div>
 
-    ##DIFF
-
-    <div class="table">
-        <div id="body" class="diffblock">
-            <div style="white-space:pre-wrap;padding:5px">${h.literal(c.pull_request.description)}</div>
-        </div>
-        <div id="changeset_compare_view_content">
-            ##CS
-            <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
-            <%include file="/compare/compare_cs.html" />
-
-            ## FILES
-            <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
-            <div class="cs_files">
-              %for fid, change, f, stat in c.files:
-                  <div class="cs_${change}">
-                    <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
-                    <div class="changes">${h.fancy_file_stats(stat)}</div>
+    ## REVIEWERS
+    <div>
+      <div class="table" style="float:right;width:46%;clear:none">
+          <div id="body" class="diffblock">
+              <div style="white-space:pre-wrap;padding:5px">${_('Pull request reviewers')}</div>
+          </div>
+          <div style="border: 1px solid #CCC">
+            <div class="group_members_wrap">
+              <ul class="group_members">
+              %for user,status in c.pull_request_reviewers:
+                <li>
+                  <div class="group_member">
+                    <div style="float:left;padding:3px">
+                      <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
+                    </div>
+                    <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,20)}"/> </div>
+                    <div style="float:left">${user.username}</div>
                   </div>
+                </li>
               %endfor
-            </div>
-        </div>
+              </ul>
+            </div>                
+          </div>
+      </div>
+      ##DIFF
+      <div class="table" style="float:left;width:46%;clear:none">
+          <div id="body" class="diffblock">
+              <div style="white-space:pre-wrap;padding:5px">${h.literal(c.pull_request.description)}</div>
+          </div>
+          <div id="changeset_compare_view_content">
+              ##CS
+              <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
+              <%include file="/compare/compare_cs.html" />
+  
+              ## FILES
+              <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
+              <div class="cs_files">
+                %for fid, change, f, stat in c.files:
+                    <div class="cs_${change}">
+                      <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
+                      <div class="changes">${h.fancy_file_stats(stat)}</div>
+                    </div>
+                %endfor
+              </div>
+          </div>
+      </div>
     </div>
     <script>
     var _USERS_AC_DATA = ${c.users_array|n};