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.
Diff revision 1 (Latest)
- /trunk/reviewboard/reviews/db.py: 1 change [ deleted content ]
- /trunk/reviewboard/reviews/feeds.py: 4 changes [ 1 2 3 4 ]
- /trunk/reviewboard/reviews/forms.py: 6 changes [ 1 2 3 4 5 6 ]
- /trunk/reviewboard/reviews/models.py: 4 changes [ 1 2 3 4 ]
- /trunk/reviewboard/reviews/templatetags/reviewtags.py: 7 changes [ 1 2 3 4 5 6 7 ]
- /trunk/reviewboard/reviews/urls/json.py: 10 changes [ 1 2 3 4 5 6 7 8 9 10 ]
| /trunk/reviewboard/reviews/feeds.py | |||
|---|---|---|---|
| Revision 910 | New Change | ||
| 1 |
|
1 |
|
| 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" |
| ... | 16 lines hidden [Expand] | ||
| 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: |
| ... | 9 lines hidden [Expand] | ||
| 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): |
| ... | 10 lines hidden [Expand] | ||
| 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 |
|
1 |
|
| 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 | ||
| ... | 32 lines hidden [Expand] | ||
| 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 | }, |
| ... | 45 lines hidden [Expand] | ||
| /trunk/reviewboard/reviews/models.py | |||
|---|---|---|---|
| Revision 910 | New Change | ||
| ... | 10 lines hidden [Expand] | ||
| 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, |
| ... | 41 lines hidden [Expand] | ||
| 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'), |
| ... | 30 lines hidden [Expand] | ||
| 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 | ||
| ... | 9 lines hidden [Expand] | ||
| 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, |
| ... | 273 lines hidden [Expand] | ||