The Checklist Extension
Review Request #5007 — Created Nov. 18, 2013 and submitted
The Checklist extension allows users to create a checklist when they are reviewing a request. This is useful for cases when there are many things to check for, and the reviewer may want to keep track of them. The app allows:
- auto creation of a new checklist upon viewing a new review request
- adding new items to the checklist
- editing existing items
- deleting items
- checking off items
- undoing checked items
- minimizing checklist
- maximizing checklist
- deleting checklist
The extension is a seperate Django app, and does not modify any of the Reviewboard code. It can be enabled through the Admin page, in the Extension tab.
Manual testing on Chrome and Firefox:
- created checklists by viewing new review requests
- added items on the checklist
- deleted items on checklist
- modified items on checklist
- checking off items
- undoing checked items
- tested on different file attachments but same requests
- tested on different review requests
- tested on different users
Description | From | Last Updated |
---|---|---|
If I click this button, the checklist is permanently deleted. 1) When permanently deleting something, I think we should get … |
mike_conley | |
Because all your view does is render a template, you can currently use the built-in template view: from django.conf.urls.defaults import … |
david | |
Why do you need this? |
david | |
There's a bunch of cleanup you can do here: In order to properly handle local sites, you should use ReviewRequestResource.get_object … |
david | |
Django provides a nice facility for this: checklist, _ = ReviewChecklist.objects.get_or_create( user=this_user, review_request=this_request) |
david | |
Left-over debug code? |
david | |
Similar comments with how to fetch and name user and review_request as the create method above. |
david | |
Instead of pulling these from kwargs, you can define them in your method signature as optional parameters and then check … |
david | |
Left-over debug code? |
david | |
This should probably check that request.user is the same as the checklist user. Right now this allows anyone to delete … |
david | |
There should be a blank line between these, since 'checklist' is the local module. |
david | |
Instead of having these in the docstring, perhaps define them as help_text on each of the fields? |
david | |
Trailing whitespace. |
david | |
Because your attributes have the same name on the client side as on the server, this should be pretty trivial: … |
david | |
This should use === instead of == |
david | |
I'm not sure why you have the cehcklist_id field when 'id' will be the same. |
david | |
This looks like left-over debug code? |
david | |
Left-over debug code? |
david | |
Could you just connect the 'change' event on the model to this.render()? |
david | |
Left-over debug code? |
david | |
=== null |
david | |
Left-over debug code? |
david | |
Left-over debug code? |
david | |
Left-over debug code? |
david | |
Left-over debug code? |
david | |
Left-over debug code? |
david | |
Alignment is a little bit off here. |
david | |
Leftover debug code. |
david | |
This should also be decorated with @webapi_login_required |
david | |
If you're not using @augment_method_from, you don't need to define this at all (the ones in the Review Board codebase … |
david | |
This should be decorated with @webapi_login_required and @webapi_check_local_site. |
david | |
This should also be decorated with @webapi_check_local_site. |
david | |
When things are in parentheses, they don't need continuation lines (the ). Your code is also wider than 80 columns … |
david | |
1 space between # and the comment. |
david | |
If you wrap the whole conditional in parentheses, you can get rid of the . |
david | |
Space between the # and the comment. Also "checklist" is tyoped. |
david | |
Hmm. Actually, request.user is already a User object. How about this? return checklist.user == request.user |
david | |
Alignment is a bit off here. Should be like this: TemplateHook(self, 'base-scripts-post', 'checklist/template.html', apply_to=[...]) Feel free to wrap the items … |
david | |
We don't put spaces between "function" and "(". Please change that here and in the rest of the implementation. |
david | |
Leftover debug code? |
david | |
This should use === |
david | |
Leftover debug code? |
david | |
Not being used... |
mike_conley | |
Not being used... |
mike_conley | |
Not being used... |
mike_conley | |
Not being used... |
mike_conley | |
You don't need Djblets' webapi_login_required, since you already get it from reviewboard.webapi.decorators. |
mike_conley | |
Not being used... |
mike_conley | |
Not being used... |
mike_conley | |
Whoa, conflict. I think you just need reviewboard.webapi.base WebAPIResource - you don't need djblets.webapi.resource's WebAPIResource too. |
mike_conley | |
Add another blank line here |
david | |
Remove this blank line. |
david | |
The alignment isn't quite right here. How about this: checklist = ReviewChecklist.objects.filter( user=user, review_request=review_request) |
david | |
Indent this line one additional space. |
david | |
Not being used |
mike_conley | |
Not being used |
mike_conley | |
Can you wrap these in () instead of using the character? |
david | |
URLHook is not being used. |
mike_conley | |
ChecklistResource is not being used. |
mike_conley | |
Remove the space between the key name and the :, and deindent the next two lines one space to match. |
david | |
One additional blank line here. |
david | |
I suggest changing this to use LESS instead of CSS. Then you could nest some of these rules. |
david | |
These should use relative paths, in case static media is hosted at a different path. Given your current layout of … |
david | |
Remove this blank line. |
david | |
Can you add SITE_ROOT + in front of the URL here? SITE_ROOT should already be in the global javascript namespace. |
david | |
Comments about methods should be before the method instead of inside it. |
david | |
Same here about method comments. |
david | |
Because you're passing this as the context for _.each, you shouldn't need to use self. |
david | |
=== |
david | |
Same here w/ method comment. |
david | |
This doesn't just "render", it also creates. |
david | |
Remove this line. |
david | |
If this doesn't have any configuration, and you're not planning on adding it right away, you should clean this up … |
david | |
The new js_bundles stuff might be nice to use here, instead of including them by hand. |
david | |
This should probably be something like "Extension for Review Board which adds checklists for reviewers" |
david | |
This should be you, not None. :) |
mike_conley | |
I think we can remove this newline. |
mike_conley | |
var saveOptions |
mike_conley | |
var saveOptions |
mike_conley | |
I think you should break this up with string concatenation, like: var response = confirm("This action will delete the entire … |
mike_conley | |
This can just be: if (response) { } |
mike_conley | |
var saveOptions |
mike_conley | |
With the js_bundle stuff, does this still need to be in here? |
mike_conley | |
Maybe format like: new Checklist.ChecklistView({ user_id: "{{user.pk}}", review_request_id: "{{review_request.id}}" }); |
mike_conley | |
Space after the hashes. |
chipx86 | |
Blank line after. |
chipx86 | |
Wrap to 79 columns. Also, typo on "reviewing requests" ("revewing requests"). |
chipx86 | |
It's going to require 2.0, because of all the new bundle stuff. |
chipx86 | |
Let's call it an alpha for now, since it's not used in production and would still need real-world testing. |
chipx86 | |
This is assigning the Python builtin function called id, which isn't what you want. It should be id. However, this … |
chipx86 | |
Make sure your code wraps to 79 chars. |
chipx86 | |
No need to do the +. Python will intelligently merge those strings together. |
chipx86 | |
Parens aren't needed. |
chipx86 | |
Same with parens here and below. Also, no blank lines between the conditionals when they're part of the same block. … |
chipx86 | |
I'd put this up by has_access_permissions. |
chipx86 | |
The register_ and unregister_ should be aligned. |
chipx86 | |
Two blank lines. |
chipx86 | |
Space before {. Same with some rules below. |
chipx86 | |
Space after :. Same with some rules below. |
chipx86 | |
You can combine this into one statement. |
chipx86 | |
You can inline this: return { user_id: ..., review_request_id: ..., ... }; |
chipx86 | |
Why did you need to override parse, instead of just providing a parseResourceData? If there's a special reason, it's best … |
chipx86 | |
No need to define options. Backbone will ignore it and overwrite it. Instead, what you should do is pull out … |
chipx86 | |
Remove the trailing comma. Some browsers will complain about this otherwise. |
chipx86 | |
Should align these. |
chipx86 | |
Best to define a template on the view and use that: template: _.template([ '<p>...</p>', '<a>...</a>', ... ].join('')), ... this.$el.html(this.template()); |
chipx86 | |
You can use method chaining: $('#checklist_toggle_size') .css(...) .attr(...); |
chipx86 | |
No need to quote this. |
chipx86 | |
Missing a var statement. |
chipx86 | |
Blank line isn't needed. |
chipx86 | |
Should align these. |
chipx86 | |
You shoud be able to make use of the bundles now. I think you mentioned it during a meeting. Also, … |
chipx86 | |
Spaces after the colon. |
chipx86 |
-
-
Because all your view does is render a template, you can currently use the built-in template view:
from django.conf.urls.defaults import patterns, url from django.views.generic import TemplateView urlpatterns = patterns( '', url(r'^$', TemplateView.as_view(template_name="configure.html")), )
You can then get rid of your views.py entirely.
-
-
There's a bunch of cleanup you can do here:
- In order to properly handle local sites, you should use
ReviewRequestResource.get_object to fetch the ReviewRequest.
This means you'll have to import that resource.
ReviewRequestResource.get_object takes in review_request_id
and local_site_name parameters, which are helpfully already
in your kwargs. You can leave them in there and just pass the
whole kwargs to get_object().- The
user_id
field can be a parameter to your function. - I'd rename 'this_user' and 'this_request' to be 'user' and 'review_request', respectively.
The new code might look something like this:
```python
... way up top:
from reviewboard.webapi.resources import resources... and down here:
def create(self, request, user_id=None, args, kwargs):
user = User.objects.get(pk=user_id)
review_request = resources.review_request.get_object(
request, args, **kwargs) - In order to properly handle local sites, you should use
-
Django provides a nice facility for this:
checklist, _ = ReviewChecklist.objects.get_or_create( user=this_user, review_request=this_request)
-
-
-
Instead of pulling these from kwargs, you can define them in your method signature as optional parameters and then check against None:
def update(self, request, checklist_id, checklist_item_id=None, item_description=None): ... if (checklist_item_id is None and item_description is not None): checklist.add_item(item_description)
Similar for the other things below that you use kwargs for.
-
-
This should probably check that request.user is the same as the checklist user. Right now this allows anyone to delete anyone else's checklist.
-
-
-
-
Because your attributes have the same name on the client side as on the server, this should be pretty trivial:
```javascript
toJSON: function() {
return this.attributes;
},
```` -
-
-
-
-
-
-
-
-
-
-
-
- Change Summary:
-
Modified the code as suggested. Also fixed some of the front-end bugs like fixed-height after maximizing, and empty string item descriptions.
- Diff:
-
Revision 2 (+748)
-
Can you move the image files into "static/images/" ?
-
-
-
-
If you're not using @augment_method_from, you don't need to define this at all (the ones in the Review Board codebase that use augment use it to override the docstring)
-
-
-
When things are in parentheses, they don't need continuation lines (the ). Your code is also wider than 80 columns here, which we try to avoid.
-
-
-
-
Hmm. Actually, request.user is already a User object. How about this?
return checklist.user == request.user
-
Alignment is a bit off here. Should be like this:
TemplateHook(self, 'base-scripts-post', 'checklist/template.html', apply_to=[...])
Feel free to wrap the items in the apply_to list to keep things under 80 columns.
-
We don't put spaces between "function" and "(". Please change that here and in the rest of the implementation.
-
-
-
- Change Summary:
-
Fixed the issues brought up from the last request. Also fixed some bugs like Delete not removing the view.
- Diff:
-
Revision 3 (+757)
-
This review is mostly small style issues.
-
-
-
The alignment isn't quite right here. How about this:
checklist = ReviewChecklist.objects.filter( user=user, review_request=review_request)
-
-
-
Remove the space between the key name and the :, and deindent the next two lines one space to match.
-
-
-
These should use relative paths, in case static media is hosted at a different path. Given your current layout of static media, probably
url('../plus.png')
I still think you should move images into a static/images/ directory.
-
-
Can you add
SITE_ROOT +
in front of the URL here?SITE_ROOT
should already be in the global javascript namespace. -
-
-
-
-
-
-
-
If this doesn't have any configuration, and you're not planning on adding it right away, you should clean this up and remove this, the url, and the is_configurable flag.
-
-
This should probably be something like "Extension for Review Board which adds checklists for reviewers"
-
I've been fiddling with your extension this week, Elaine. Very cool. :) Just some notes...
-
If I click this button, the checklist is permanently deleted.
1) When permanently deleting something, I think we should get the user to confirm this choice before carrying it out.
2) How do I use a checklist again if I've deleted it? How can I start using a checklist again?
-
-
-
-
-
You don't need Djblets' webapi_login_required, since you already get it from reviewboard.webapi.decorators.
-
-
-
Whoa, conflict. I think you just need reviewboard.webapi.base WebAPIResource - you don't need djblets.webapi.resource's WebAPIResource too.
-
-
-
-
-
-
Hey Elaine,
I know you've closed a bunch of these issues, but your posted revision still has them. Have you forgotten to publish your update?
-Mike
- Change Summary:
-
Wrote a more comprehensive description of my extension.
- Summary:
-
The Checklist Extension - Non WIPThe Checklist Extension
- Description:
-
~ The complete code for Checklist Extension, including new API code.
~ The Checklist extension allows users to create a checklist when they are reviewing a request. This is useful for cases when there are many things to check for, and the reviewer may want to keep track of them. The app allows:
+ + - auto creation of a new checklist upon viewing a new review request
+ - adding new items to the checklist
+ - editing existing items
+ - deleting items
+ - checking off items
+ - undoing checked items
+ - minimizing checklist
+ - maximizing checklist
+ - deleting checklist
+ + The extension is a seperate Django app, and does not modify any of the Reviewboard code. It can be enabled through the Admin page, in the Extension tab.
- Testing Done:
-
~ Manual testing on Chrome and Firefox. Created and deleted checklists. Added, deleted, and edited checklist items. Tested on the same review requests to check if status is saved properly. Tested on different review requests to check if different checklists are used.
~ Manual testing on Chrome and Firefox:
+ - created checklists by viewing new review requests + - added items on the checklist + - deleted items on checklist + - modified items on checklist + - checking off items + - undoing checked items + - tested on different file attachments but same requests + - tested on different review requests + - tested on different users
-
Really minor nits from me now, but this looks really good. There are some whitespace issues that you need to clean-up though - just check for the large red blobs in the diff.
-
-
-
-
I think you should break this up with string concatenation, like:
var response = confirm("This action will delete the entire checklist." +
"This cannot be undone."); -
-
-
-
Maybe format like:
new Checklist.ChecklistView({
user_id: "{{user.pk}}",
review_request_id: "{{review_request.id}}"
});
- Change Summary:
-
I edited the code based on new reviews. I'm still trying to get the bundles working on the template.
- Diff:
-
Revision 5 (+828)
-
Looking good. Got some style stuff to fix up. Sorry I didn't get that feedback to you sooner (my fault).
Something I recommend is to run the 'pep8' and 'pyflakes' tools on your Python codebase, and the 'jshint' tool on your JavaScript codebase. These are great for finding common style issues and telling you what needs fixing.
Overall though, this is looking like a valuable extension! Good job :)
-
-
-
-
-
Let's call it an alpha for now, since it's not used in production and would still need real-world testing.
-
This is assigning the Python builtin function called
id
, which isn't what you want. It should beid
. However, this is the default, so I think you can just remove the line. -
-
-
-
Same with parens here and below.
Also, no blank lines between the conditionals when they're part of the same block. So:
if foo: ... elif bar: ...
-
-
-
-
-
-
-
-
Why did you need to override
parse
, instead of just providing aparseResourceData
? If there's a special reason, it's best to document it. -
No need to define
options
. Backbone will ignore it and overwrite it.Instead, what you should do is pull out every option you care about inside
initialize
below. It'll be passed in anoptions
variable containing what was passed to the view. You can set each of these onthis
.The next version of Backbone.js will remove the automatic assignment of
this.options
, so it's important. (We still need to update our code to do this as well).Same for other views.
-
-
-
Best to define a template on the view and use that:
template: _.template([ '<p>...</p>', '<a>...</a>', ... ].join('')),
...
this.$el.html(this.template());
-
-
-
-
-
-
You shoud be able to make use of the bundles now. I think you mentioned it during a meeting.
Also,
/static/
isn't always going to be a valid path. Bundles will take care of the path for you, but if you were to use your own script and link tags, you'd need to do:{% static "ext/checklist.extension.Checklist/..." %}
That will ensure you have the right path for the configuration.
-