Review Board

beta

Replace reviews.db with a ReviewRequestManager

Updated 1 year, 4 months ago

Christian Hammond Reviewers
trunk reviewboard
None Review Board SVN
We had several utility functions in reviews.db, but these should have gone in a ReviewRequestManager. Added one and ported all the code over.
Review request creation and updating still works, as does the dashboard, lists, diff view and review request detail page.
/trunk/reviewboard/reviews/db.py
Revision 910 New Change
1
from django.db.models import Q
2
3
from reviewboard.diffviewer.models import DiffSetHistory
4
from reviewboard.reviews.models import ReviewRequest
5
6
def _get_review_request_list(user, status, extra_query=None):
7
    query = Q(public=True)
8
9
    if user and user.is_authenticated():
10
        query = query | Q(submitter=user)
11
12
    if status:
13
        query = query & Q(status=status)
14
15
    if extra_query:
16
        query = query & extra_query
17
18
    return ReviewRequest.objects.filter(query).distinct()
19
20
21
def get_all_review_requests(user=None, status='P'):
22
    return _get_review_request_list(user, status)
23
24
25
def get_review_requests_to_group(group_name, user=None, status='P'):
26
    return _get_review_request_list(user, status,
27
                                    Q(target_groups__name=group_name))
28
29
30
def get_review_requests_to_user_groups(username, user=None, status='P'):
31
    return _get_review_request_list(user, status,
32
                                    Q(target_groups__users__username=username))
33
34
35
def get_review_requests_to_user_directly(username, user=None, status='P'):
36
    return _get_review_request_list(user, status,
37
                                    Q(target_people__username=username))
38
39
40
def get_review_requests_from_user(username, user=None, status='P'):
41
    return _get_review_request_list(user, status,
42
                                    Q(submitter__username=username))
43
44
45
def get_review_requests_to_user(username, user=None, status='P'):
46
    # Using an OR query inside the extra_query field like this:
47
    # Q(target_people__username=username) |
48
    #     Q(target_groups__users__username=username))
49
    # does not work.  I haven't exactly figured out why.
50
51
    # This is disgusting, but it actually works =P
52
    # FIXME: it might be useful to cache this and invalidate the cache every
53
    #        time the status on a review request changes.
54
    results = []
55
    def add_if_unique(requests):
56
        for request in requests:
57
            found = False
58
            for result in results:
59
                if request.id == result.id:
60
                    found = True
61
            if not found:
62
                results.append(request)
63
64
    add_if_unique(get_review_requests_to_user_groups(username, user, status))
65
    add_if_unique(get_review_requests_to_user_directly(username, user, status))
66
    results.sort(lambda a, b: cmp(a.last_updated, b.last_updated),
67
                 reverse=True)
68
    return results
69
70
71
def get_review_requests_from_user(username, user=None, status='P'):
72
    return _get_review_request_list(user, status,
73
                                    Q(submitter__username=username))
74
75
76
class InvalidChangeNumberError(Exception):
77
    def __init__(self):
78
        Exception.__init__(self, None)
79
80
81
class ChangeNumberInUseError(Exception):
82
    def __init__(self, review_request=None):
83
        Exception.__init__(self, None)
84
        self.review_request = review_request
85
86
87
def update_review_request_from_changenum(review_request, changenum):
88
    changeset = \
89
        review_request.repository.get_scmtool().get_changeset(changenum)
90
91
    if not changeset:
92
        raise InvalidChangeNumberError()
93
94
    review_request.changenum = changenum
95
    review_request.summary = changeset.summary
96
    review_request.description = changeset.description
97
    review_request.testing_done = changeset.testing_done
98
    review_request.branch = changeset.branch
99
    review_request.bugs_closed = ','.join(changeset.bugs_closed)
100
101
102
def create_review_request(user, repository, changenum=None):
103
    try:
104
        review_request = ReviewRequest.objects.get(changenum=changenum,
105
                                                   repository=repository)
106
        raise ChangeNumberInUseError(review_request)
107
    except ReviewRequest.DoesNotExist:
108
        pass
109
110
    review_request = ReviewRequest(repository=repository)
111
112
    if changenum:
113
        update_review_request_from_changenum(review_request, changenum)
114
115
    diffset_history = DiffSetHistory()
116
    diffset_history.save()
117
118
    review_request.diffset_history = diffset_history
119
    review_request.submitter = user
120
    review_request.status = 'P'
121
    review_request.public = False
122
    review_request.save()
123
124
    return review_request
/trunk/reviewboard/reviews/feeds.py
Revision 910 New Change
1
from django.contrib.auth.models import User
1
from django.contrib.auth.models import User
2
from django.contrib.syndication.feeds import Feed
2
from django.contrib.syndication.feeds import Feed
3
from django.core.exceptions import ObjectDoesNotExist
3
from django.core.exceptions import ObjectDoesNotExist
4
from django.utils.feedgenerator import Atom1Feed
4
from django.utils.feedgenerator import Atom1Feed
5
5
6
from reviewboard.reviews.db import get_all_review_requests, \
7
                                   get_review_requests_to_user_directly, \
8
                                   get_review_requests_to_group
9
from reviewboard.reviews.models import Group
6
from reviewboard.reviews.models import Group
10
7
11
class BaseReviewFeed(Feed):
8
class BaseReviewFeed(Feed):
12
    title_template = "feeds/reviews_title.html"
9
    title_template = "feeds/reviews_title.html"
13
    description_template = "feeds/reviews_description.html"
10
    description_template = "feeds/reviews_description.html"
30
    title = "Review Requests"
27
    title = "Review Requests"
31
    link = "/r/all/"
28
    link = "/r/all/"
32
    description = "All pending review requests."
29
    description = "All pending review requests."
33
30
34
    def items(self):
31
    def items(self):
35
        return get_all_review_requests()[:20]
32
        return ReviewRequest.objects.public()[:20]
36
33
37
34
38
class RssSubmitterReviewsFeed(BaseReviewFeed):
35
class RssSubmitterReviewsFeed(BaseReviewFeed):
39
    def get_object(self, bits):
36
    def get_object(self, bits):
40
        if len(bits) != 1:
37
        if len(bits) != 1:
50
47
51
    def description(self, submitter):
48
    def description(self, submitter):
52
        return "Pending review requests to %s" % submitter
49
        return "Pending review requests to %s" % submitter
53
50
54
    def items(self, submitter):
51
    def items(self, submitter):
55
        return get_review_requests_to_user_directly(submitter.username).\
52
        return ReviewRequest.objects.to_user_directly(submitter.username).\
56
            order_by('-last_updated')[:20]
53
            order_by('-last_updated')[:20]
57
54
58
55
59
class RssGroupReviewsFeed(BaseReviewFeed):
56
class RssGroupReviewsFeed(BaseReviewFeed):
60
    def get_object(self, bits):
57
    def get_object(self, bits):
71
68
72
    def description(self, group):
69
    def description(self, group):
73
        return "Pending review requests to %s" % group
70
        return "Pending review requests to %s" % group
74
71
75
    def items(self, group):
72
    def items(self, group):
76
        return get_review_requests_to_group(group).\
73
        return ReviewRequest.objects.to_group(group).\
77
            order_by('-last_updated')[:20]
74
            order_by('-last_updated')[:20]
78
75
79
76
80
# Atom feeds
77
# Atom feeds
81
class AtomReviewsFeed(RssReviewsFeed):
78
class AtomReviewsFeed(RssReviewsFeed):
82
    feed_type = Atom1Feed
79
    feed_type = Atom1Feed
83
80
84
class AtomSubmitterReviewsFeed(RssSubmitterReviewsFeed):
81
class AtomSubmitterReviewsFeed(RssSubmitterReviewsFeed):
85
    feed_type = Atom1Feed
82
    feed_type = Atom1Feed
86
83
87
class AtomGroupReviewsFeed(RssGroupReviewsFeed):
84
class AtomGroupReviewsFeed(RssGroupReviewsFeed):
88
    feed_type = Atom1Feed
85
    feed_type = Atom1Feed
/trunk/reviewboard/reviews/forms.py
Revision 910 New Change
1
import re
1
import re
2
2
3
from django import newforms as forms
3
from django import newforms as forms
4
from PIL import Image
4
from PIL import Image
5
5
6
from reviewboard.diffviewer.forms import UploadDiffForm, EmptyDiffError
6
from reviewboard.diffviewer.forms import UploadDiffForm, EmptyDiffError
7
from reviewboard.reviews.models import ReviewRequest, \
7
from reviewboard.reviews.models import ReviewRequest, \
8
                                       ReviewRequestDraft, Screenshot
8
                                       ReviewRequestDraft, Screenshot, \
9
from reviewboard.scmtools.models import Repository
10
from reviewboard.reviews.db import create_review_request, \
11
                                   update_review_request_from_changenum, \
12
                                   ChangeNumberInUseError
9
                                       ChangeNumberInUseError
10
from reviewboard.scmtools.models import Repository
13
11
14
12
15
class OwnershipError(ValueError):
13
class OwnershipError(ValueError):
16
    pass
14
    pass
17
15
50
            except NotImplementedError:
48
            except NotImplementedError:
51
                # This scmtool doesn't have changesets
49
                # This scmtool doesn't have changesets
52
                pass
50
                pass
53
51
54
        try:
52
        try:
55
            review_request = \
53
            review_request = ReviewRequest.objects.create(user, repository,
56
                create_review_request(user, repository, changenum)
54
                                                          changenum)
57
        except ChangeNumberInUseError:
55
        except ChangeNumberInUseError:
58
            review_request = \
56
            review_request = ReviewRequest.objects.get(changenum=changenum)
59
                ReviewRequest.objects.get(changenum=changenum)
57
            review_request.update_from_changenum(changenum)
60
            update_review_request_from_changenum(review_request, changenum)
58
61
            if review_request.status == 'D':
59
            if review_request.status == 'D':
62
                review_request.status = 'P'
60
                review_request.status = 'P'
63
                review_request.public = False
61
                review_request.public = False
62
64
            review_request.save()
63
            review_request.save()
65
64
66
        diff_form = UploadDiffForm(repository, data={
65
        diff_form = UploadDiffForm(repository, data={
67
            'basedir': self.cleaned_data['basedir'],
66
            'basedir': self.cleaned_data['basedir'],
68
        },
67
        },
/trunk/reviewboard/reviews/models.py
Revision 910 New Change
11
from reviewboard.scmtools.models import Repository
11
from reviewboard.scmtools.models import Repository
12
from reviewboard.utils.fields import ModificationTimestampField
12
from reviewboard.utils.fields import ModificationTimestampField
13
from utils.templatetags.htmlutils import thumbnail
13
from utils.templatetags.htmlutils import thumbnail
14
14
15
15
16
class InvalidChangeNumberError(Exception):
17
    def __init__(self):
18
        Exception.__init__(self, None)
19
20
21
class ChangeNumberInUseError(Exception):
22
    def __init__(self, review_request=None):
23
        Exception.__init__(self, None)
24
        self.review_request = review_request
25
26
16
class Group(models.Model):
27
class Group(models.Model):
17
    name = models.CharField(maxlength=64)
28
    name = models.CharField(maxlength=64)
18
    display_name = models.CharField(maxlength=64)
29
    display_name = models.CharField(maxlength=64)
19
    mailing_list = models.EmailField(blank=True)
30
    mailing_list = models.EmailField(blank=True)
20
    users = models.ManyToManyField(User, core=False, blank=True,
31
    users = models.ManyToManyField(User, core=False, blank=True,
62
    class Admin:
73
    class Admin:
63
        list_display = ('thumb', 'caption', 'image')
74
        list_display = ('thumb', 'caption', 'image')
64
        list_display_links = ('thumb', 'caption')
75
        list_display_links = ('thumb', 'caption')
65
76
66
77
78
class ReviewRequestManager(models.Manager):
79
    """
80
    A manager for review requests. Provides specialized queries to retrieve
81
    review requests with specific targets or origins, and to create review
82
    requests based on certain data.
83
    """
84
85
    def create(self, user, repository, changenum=None):
86
        """
87
        Creates a new review request, optionally filling in fields based off
88
        a change number.
89
        """
90
        if changenum:
91
            try:
92
                review_request = self.get(changenum=changenum,
93
                                          repository=repository)
94
                raise ChangeNumberInUseError(review_request)
95
            except ReviewRequest.DoesNotExist:
96
                pass
97
98
        review_request = ReviewRequest(repository=repository)
99
100
        if changenum:
101
            review_request.update_from_changenum(changenum)
102
103
        diffset_history = DiffSetHistory()
104
        diffset_history.save()
105
106
        review_request.diffset_history = diffset_history
107
        review_request.submitter = user
108
        review_request.status = 'P'
109
        review_request.public = False
110
        review_request.save()
111
112
        return review_request
113
114
    def public(self, user=None, status='P'):
115
        return self._query(user, status)
116
117
    def to_group(self, group_name, user=None, status='P'):
118
        return self._query(user, status, Q(target_groups__name=group_name))
119
120
    def to_user_groups(self, username, user=None, status='P'):
121
        return self._query(user, status,
122
                           Q(target_groups__users__username=username))
123
124
    def to_user_directly(self, username, user=None, status='P'):
125
        return self._query(user, status, Q(target_people__username=username))
126
127
    def to_user(self, username, user=None, status='P'):
128
        # Using an OR query inside the extra_query field like this:
129
        # Q(target_people__username=username) |
130
        #     Q(target_groups__users__username=username))
131
        # does not work.  I haven't exactly figured out why.
132
133
        # This is disgusting, but it actually works =P
134
        # FIXME: it might be useful to cache this and invalidate the cache every
135
        #        time the status on a review request changes.
136
        results = []
137
        def add_if_unique(requests):
138
            for request in requests:
139
                found = False
140
                for result in results:
141
                    if request.id == result.id:
142
                        found = True
143
                if not found:
144
                    results.append(request)
145
146
        add_if_unique(self.to_user_groups(username, user, status))
147
        add_if_unique(self.to_user_directly(username, user, status))
148
        results.sort(lambda a, b: cmp(a.last_updated, b.last_updated),
149
                     reverse=True)
150
        return results
151
152
    def from_user(self, username, user=None, status='P'):
153
        return self._query(user, status, Q(submitter__username=username))
154
155
    def _query(self, user, status, extra_query=None):
156
        query = Q(public=True)
157
158
        if user and user.is_authenticated():
159
            query = query | Q(submitter=user)
160
161
        if status:
162
            query = query & Q(status=status)
163
164
        if extra_query:
165
            query = query & extra_query
166
167
        return self.filter(query).distinct()
168
169
67
class ReviewRequest(models.Model):
170
class ReviewRequest(models.Model):
68
    STATUSES = (
171
    STATUSES = (
69
        ('P', 'Pending Review'),
172
        ('P', 'Pending Review'),
70
        ('S', 'Submitted'),
173
        ('S', 'Submitted'),
71
        ('D', 'Discarded'),
174
        ('D', 'Discarded'),
102
                                         related_name="review_request",
205
                                         related_name="review_request",
103
                                         core=False, blank=True)
206
                                         core=False, blank=True)
104
    inactive_screenshots = models.ManyToManyField(Screenshot,
207
    inactive_screenshots = models.ManyToManyField(Screenshot,
105
        related_name="inactive_review_request", core=False, blank=True)
208
        related_name="inactive_review_request", core=False, blank=True)
106
209
210
211
    # Set this up with the ReviewRequestManager
212
    objects = ReviewRequestManager()
213
214
107
    def get_bug_list(self):
215
    def get_bug_list(self):
108
        bugs = re.split(r"[, ]+", self.bugs_closed)
216
        bugs = re.split(r"[, ]+", self.bugs_closed)
109
        bugs.sort(cmp=lambda x,y: int(x) - int(y))
217
        bugs.sort(cmp=lambda x,y: int(x) - int(y))
110
        return bugs
218
        return bugs
111
219
121
                    public=True,
229
                    public=True,
122
                    timestamp__gt=visit.timestamp).exclude(user=user)
230
                    timestamp__gt=visit.timestamp).exclude(user=user)
123
231
124
        return self.review_set.get_empty_query_set()
232
        return self.review_set.get_empty_query_set()
125
233
234
    def update_from_changenum(self, changenum):
235
        changeset = self.repository.get_scmtool().get_changeset(changenum)
236
237
        if not changeset:
238
            raise InvalidChangeNumberError()
239
240
        self.changenum = changenum
241
        self.summary = changeset.summary
242
        self.description = changeset.description
243
        self.testing_done = changeset.testing_done
244
        self.branch = changeset.branch
245
        self.bugs_closed = ','.join(changeset.bugs_closed)
126
246
127
    @permalink
247
    @permalink
128
    def get_absolute_url(self):
248
    def get_absolute_url(self):
129
        return ('reviewboard.reviews.views.review_detail', None, {
249
        return ('reviewboard.reviews.views.review_detail', None, {
130
            'review_request_id': self.id,
250
            'review_request_id': self.id,
/trunk/reviewboard/reviews/templatetags/reviewtags.py
Revision 910 New Change
1
from django import template
1
from django import template
2
from django.db.models import Q
2
from django.db.models import Q
3
from django.db.models.query import QuerySet
3
from django.db.models.query import QuerySet
4
from django.template import resolve_variable
4
from django.template import resolve_variable
5
from django.template import NodeList, TemplateSyntaxError, VariableDoesNotExist
5
from django.template import NodeList, TemplateSyntaxError, VariableDoesNotExist
6
from django.template.loader import render_to_string
6
from django.template.loader import render_to_string
7
from django.utils import simplejson
7
from django.utils import simplejson
8
8
9
from djblets.util.decorators import blocktag
9
from djblets.util.decorators import blocktag
10
from reviewboard.accounts.models import ReviewRequestVisit
10
from reviewboard.accounts.models import ReviewRequest, ReviewRequestVisit
11
from reviewboard.reviews.db import get_all_review_requests, \
12
                                   get_review_requests_from_user, \
13
                                   get_review_requests_to_user, \
14
                                   get_review_requests_to_user_directly, \
15
                                   get_review_requests_to_group
16
from reviewboard.reviews.models import ReviewRequestDraft, ScreenshotComment
11
from reviewboard.reviews.models import ReviewRequestDraft, ScreenshotComment
17
from reviewboard.utils.templatetags.htmlutils import humanize_