-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow deletion of Assignment's with no groups #6880
Allow deletion of Assignment's with no groups #6880
Conversation
Although routes.rb already generated the endpoint for destroying an assignment, there was no #destroy action in controllers/api/assignments_controller.rb. An authorized instructor should be able to delete an assignment if and only if the assignment does not have any groups. I implemented this #destroy action and wrote corresponding rspec tests in spec/controller/api/assignments_controller_spec.rb.
Added informative en.yml messages for the different possible scenarios that can arise when attempting to delete an assignment (using those in the api controller instead of the former hardcoded messages) and improved the style of the expectations in the corresponding rspec tests.
Corrected a I18 style problem and updated the Changelog (though this will change again to get the PR number).
Pull Request Test Coverage Report for Build 7932654664Details
💛 - Coveralls |
Instead of having the logic that checks if the assignment has any groups inside the api-assignments-controller, this is now taking place in the model itself, simply by including 'dependent: restrict_with_exception' in the association definition. Now, the api-assignments controller has a begin-rescue block to catch this exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimischly7 good! So in addition to this change, I want you to add a button to delete the assignment from the web interface (and more specifically from the assignment edit form.
Add the button and give it a red background using one of the built-in colour constants, so that it's very clearly distinguished from other buttons. Make sure the button is disabled if there are any groups, and that there is a confirmation dialog box. This will need a parallel assignments controller route on the non-API controller.
Changelog.md
Outdated
@@ -7,6 +7,7 @@ | |||
- Bug Fix: Prevent assigning inactive graders to a group (#6801) | |||
- Added new API routes for the `create` and `destroy` actions of SectionsController (#6873) | |||
- Display error message in real-time when running automated tests, rather than waiting for page refresh (#6878) | |||
- - Added `destroy` action to `controllers/api/assignments_controller` (#6880) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an extra -
here. Also, please do a pull from master; we've made a new minor release, so you'll need to add this to the new [unreleased]
section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems you missed this
Synchronization-with-upstream
Added a destroy action to the non-API Assignments controller - and the corresponding route in routes.rb, to be used by the UI (this non-API route will be used for deletion of assignments from the UI). Recall that an assignment should be deleted only if it has no groups. This logic is handled in the model through an appropriate association feature. Nevertheless, this method must NOT be called on an assignment with groups. Its precondition is that the Assignment object with which it is called has no groups. This should be reasonable; for example, the delete button for an assignment should be visible only if the assignment has no groups.
All elements in the assignment-edit page are found inside the _form.html.erb partial. There, added a new button to delete assignments using the button_to helper, and the button makes a DELETE request to the destroy action of the non-API assignments controller. The button is conditionally rendered based on whether the assignment has any groups (using ERB's templating capabilities). It is visible only if the corresponding assignment has no groups.
CSS class for making the assignment-delete button red, with bold font. This is just temporary and subject to change. Its style does not really align with the general style of Markus' UI.
The red button should only be rendered if (1) you are in the assignment-edit page and (2) the current asisgnment has no groups. Previously, only (2) was being checked (i.e. that the assignment has no groups). So if, for example, you went to create a new assignment, the condition of the if statement did not fail and rails attempted to render the button, leading to error because in most cases @assignment is undefined (nil). I added the condition that we are in the context of the edit action of the assignment controller to face this issue (only in that case will the delete button be rendered).
…ent-destroy-task Merging upstream.
Utilised the external class FlashResponder class to automatically place the appropriate success message in the ActionDispatch:Flash object. This strategy already takes place in the Assignments controller in the #create and #update actions. In particular, given the line <respond_with @assignment ...>, the FlashResponder checks the status of the @assignment --that is, if it was just created, updated, or destroyed-- and places the appropriate message in the flash object (which should be defined in the appropriate format in a en.yml file). Then, by definition of how the flash object works, the next (redirected-to) action (and thus the view rendered by the next action) has access to the data stored in flash. In this particular case, the assignment_content layout view renders the _flash_message.html.erb partial, and it is this partial that used the flash object (doing flash[:...]).
Handling the case where the action is called with an Assignment that still has groups. This is impossible from the UI (since the delete button only shows if the assignment does not have any groups), but it possible from direct HTTP requests.
Testing behaviour when request is not authorized (fail to destroy), when the request is authorized but the assignment still has groups (fail to destroy), and when the request is authorized and the assignment has no groups (succeeds to destroy).
…ent-destroy-task merging-with-upstream
Firstly, instead of completely hiding the assignment-delete button for assignments with groups (in their 'edit' pages), now it is visible but disabled, and when the mouse is over it a tooltip appears saying 'Assignment has groupings'. Also, in the case where the button is enabled (i.e. for assignments with no groups), I improved the style by using Markus' predefined constants (found in assets/stylesheets/common/_constants.scss) and defining a new style for the id #assignment-delete (targeting specifically that delete button); the button also is of class .button, utilising predefined styling. The only two files modified for these changes are the partial app/views/assignments/_form.html.erb and the stylesheet app/assets/stylesheets/common/_markus.scss.
change belonged to unreleased section
…ent-destroy-task updating-with-upstream
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimischly7 great work! Sorry for the delay on reviewing this. I left a few more comments for you to polish this PR.
Changelog.md
Outdated
@@ -7,6 +7,7 @@ | |||
- Bug Fix: Prevent assigning inactive graders to a group (#6801) | |||
- Added new API routes for the `create` and `destroy` actions of SectionsController (#6873) | |||
- Display error message in real-time when running automated tests, rather than waiting for page refresh (#6878) | |||
- - Added `destroy` action to `controllers/api/assignments_controller` (#6880) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems you missed this
@@ -301,6 +301,15 @@ button, | |||
min-width: 70px; | |||
padding: 0.25em 0.5em; | |||
} | |||
|
|||
&.delete-button { | |||
background: #f00; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For colours, you should always use existing colour constants (start by looking in _constants.scss
)
@@ -301,6 +301,15 @@ button, | |||
min-width: 70px; | |||
padding: 0.25em 0.5em; | |||
} | |||
|
|||
&.delete-button { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A more appropriate class name is danger-button
(connoting a "dangerous" action, which might not just be limited to deletion in the future)
assignment_id = params[:id] | ||
assignment = Assignment.find_by(id: assignment_id) | ||
if assignment.nil? | ||
render 'shared/http_status', locals: { code: '404', message: | ||
I18n.t('assignments.assignment_not_found', | ||
invalid_id: assignment_id) }, status: :not_found |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is not necessary; look at other methods in this class. In particular, the record
method will give you the Assignment
instance corresponding to params[:id]
. There's existing code that computes this and handles missing objects more generally across all controllers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given our discussion on Friday on whether we should check in the controller whether the requested record is actually valid, even though the SessionHandler
class method check_record
(as a before_action
method) already checks that, I decided to keep the extra check. Let me know if you want me to remove it.
app/views/assignments/_form.html.erb
Outdated
|
||
<!--Conditional Rendering--> | ||
<% if (@assignment.groups.length === 0) and (action_name=="edit")%> | ||
<%= button_to "Delete", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember that no string literals should appear in your code. Always define strings in locale files.
app/views/assignments/_form.html.erb
Outdated
form_class: 'display-inline-block', | ||
title: t('helpers.submit.delete', model: Assignment.model_name.human), | ||
remote: true %> | ||
<% end %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is an extra space here
app/views/assignments/_form.html.erb
Outdated
class: 'delete-button', | ||
form_class: 'display-inline-block', | ||
title: t('helpers.submit.delete', model: Assignment.model_name.human), | ||
remote: true %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the remote: true
is not necessary (I encourage you to look up this option to understand why)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I see why. If I understand correctly, by setting remote:true
we allow the generated form to be submitted asynchronously (with AJAX), but this requireds that we handle it appropriately in the respond_to
block in our action with a case for javascript; since we are not doing this in the action, there is no point.
app/views/assignments/_form.html.erb
Outdated
title: t('helpers.submit.delete', model: Assignment.model_name.human), | ||
remote: true %> | ||
<% end %> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line hasn't been deleted
expect(assignment.groups).to be_empty | ||
delete :destroy, params: { id: assignment.id, course_id: course.id } | ||
expect(response).to have_http_status(200) | ||
expect(Assignment.all).to eq([]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this assertion. The next line is more specific to what you're actually testing
…ent-destroy-task pulling-master
Most notably, simplifying the erb code in the _form partial for assignments -- for when to show the delete button (and when it should be disabled). Includes also other minor changes.
app/views/assignments/_form.html.erb
Outdated
title: t('helpers.submit.delete', model: Assignment.model_name.human), | ||
remote: true %> | ||
<% end %> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line hasn't been deleted
@@ -303,6 +303,12 @@ button, | |||
} | |||
} | |||
|
|||
#assignment-delete { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay this is better, but (1) make it a class, and (2) call the class danger-button
, which is more general.
@@ -299,6 +299,24 @@ def submit_file | |||
upload_file(grouping, only_required_files: assignment.only_required_files) | |||
end | |||
|
|||
def destroy | |||
assignment = record | |||
if assignment.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check isn't necessary, you can remove it. The check_record
helper is sufficient to check for this
@@ -657,6 +657,17 @@ def lti_settings | |||
render layout: 'assignment_content' | |||
end | |||
|
|||
def destroy | |||
@assignment = @record |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use record
(as with the other methods)
app/views/assignments/_form.html.erb
Outdated
@@ -489,4 +489,22 @@ | |||
data: { disable_with: t('working') } %> | |||
</p> | |||
<% end %> | |||
|
|||
<% if (action_name=="edit") %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If statements don't need parentheses in Ruby. Also, put spaces around ==
app/views/assignments/_form.html.erb
Outdated
<%= button_to t(:delete), | ||
course_assignment_path(@current_course, @assignment), | ||
{ | ||
data: { confirm: "Are you sure you want to delete this assignment?" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This string also needs to be internationalized
app/views/assignments/_form.html.erb
Outdated
data: { confirm: "Are you sure you want to delete this assignment?" }, | ||
method: 'delete', | ||
class: has_groups ? nil : 'button', | ||
id: has_groups ? nil : 'assignment-delete', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following my above comment, don't use an id
here, instead conditionally add a second class to 'button'
above
app/views/assignments/_form.html.erb
Outdated
disabled: has_groups | ||
} | ||
%> | ||
<%end %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a space missing after the <%
@@ -502,6 +507,35 @@ | |||
expect(response).to have_http_status(403) | |||
end | |||
end | |||
context 'DELETE assignment' do | |||
it 'should successfully delete assignment because the assignment has no groups' do | |||
assignment # since lazy let is used for creating an assignment, I invoke it here to trigger its execution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't necessary, as assignment
is immediately used on the next line
expect(Assignment.exists?(assignment.id)).to eq(false) | ||
end | ||
it 'fails to delete assignment because assignment has groups' do | ||
assignment # since lazy let is used for creating an assignment, I invoke it here to trigger its execution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above
Includes defining strings in locale file, improving the css, and small formatting fixes.
…s into assignment-destroy-task merge-origin
Motivation and Context
An authorized instructor should be able to delete an assignment if and only if the assignment does not have any groups.
Although
routes.rb
already generated the endpoint for destroying an assignment, there was no#destroy
action incontrollers/api/assignments_controller.rb
.Your Changes
Description:
I implemented a
#destroy
action incontrollers/api/assignments_controller.rb
which deletes the Assignment corresponding to the givenid
if this assignment does not have any groups. The logic for checking whether an assignment has any groups is performed at the model level using thedependent: restrict_with_exception
attribute of rails associations. The controller catches the potential exception and responds to the request accordingly.Type of change (select all that apply):
Testing
Wrote several rspec tests in
spec/controller/api/assignments_controller_spec.rb
aiming to capture the various scenarios and contexts under which one may attempt to delete an assignment.Checklist
Pull request to make documentation changes (if applicable)
MarkUsProject/Wiki#201