Skip to content

Commit

Permalink
Merge pull request #1721 from sparc-request/jl-ui-for-protocol-merge
Browse files Browse the repository at this point in the history
Jl - ui for protocol merge
  • Loading branch information
Stuart-Johnson committed Feb 20, 2019
2 parents d8c82d2 + e42936d commit 0bf8ed9
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 4 deletions.
18 changes: 18 additions & 0 deletions app/assets/javascripts/dashboard/protocol_merges.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
$ ->

$(document).on 'click', '#merge-button', ->
master_protocol_id = $('#master_protocol').val()
sub_protocol_id = $('#sub_protocol').val()

data =
'master_protocol_id' : master_protocol_id,
'sub_protocol_id' : sub_protocol_id

if confirm("Preparing to merge protocol #{master_protocol_id} and protocol #{sub_protocol_id}. Do you wish to continue?")
$.ajax
type: 'GET'
url: "/dashboard/protocol_merge/perform_protocol_merge"
data: data
success: ->
$('#master_protocol').val('')
$('#sub_protocol').val('')
139 changes: 139 additions & 0 deletions app/controllers/dashboard/protocol_merges_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Copyright © 2011-2018 MUSC Foundation for Research Development
# All rights reserved.

# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with the distribution.

# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

class Dashboard::ProtocolMergesController < Dashboard::BaseController
before_action :authorize_overlord
respond_to :json, :html

def show
@user = current_identity
end

def perform_protocol_merge
master_protocol = Protocol.where(id: params[:master_protocol_id].to_i).first
sub_protocol = Protocol.where(id: params[:sub_protocol_id].to_i).first

merge_srs = Dashboard::MergeSrs.new()
fix_ssr_ids = Dashboard::FixSsrIds.new(master_protocol)

if (master_protocol == nil) || (sub_protocol == nil)
flash[:alert] = 'Protocol(s) not found. Check IDs and try again.'
elsif (master_protocol.has_clinical_services? && sub_protocol.has_clinical_services?)
flash[:alert] = 'Both protocols have calendars. Only one protocol may have a calendar for merge to work.'
else
ActiveRecord::Base.transaction do

#transfer the project roles as needed
sub_protocol.project_roles.each do |role|
if role.role != 'primary-pi' && role_should_be_assigned?(role, master_protocol)
role.update_attributes(protocol_id: master_protocol.id)
end
end

# checking for and assigning research types, impact areas, and affiliations...
if has_research?(sub_protocol, 'human_subjects') && !has_research?(master_protocol, 'human_subjects')
sub_protocol.human_subjects_info.update_attributes(protocol_id: master_protocol.id)
elsif has_research?(sub_protocol, 'vertebrate_animals') && !has_research?(master_protocol, 'vertebrate_animals')
sub_protocol.vertebrate_animals_info.update_attributes(protocol_id: master_protocol.id)
elsif has_research?(sub_protocol, 'investigational_products') && !has_research?(master_protocol, 'investigational_products')
sub_protocol.investigational_products_info.update_attributes(protocol_id: master_protocol.id)
elsif has_research?(sub_protocol, 'ip_patents') && !has_research?(master_protocol, 'ip_patents')
sub_protocol.ip_patents_info.update_attributes(protocol_id: master_protocol.id)
end

sub_protocol.impact_areas.each do |area|
area.protocol_id = master_protocol.id
area.save(validate: false)
end

sub_protocol.affiliations.each do |affiliation|
affiliation.protocol_id = master_protocol.id
affiliation.save(validate: false)
end

# assigning service requests...
fulfillment_ssrs = []
sub_protocol.service_requests.each do |request|
request.protocol_id = master_protocol.id
request.save(validate: false)
request.sub_service_requests.each do |ssr|
ssr.update_attributes(protocol_id: master_protocol.id)
master_protocol.next_ssr_id = (master_protocol.next_ssr_id + 1)
master_protocol.save(validate: false)
if ssr.in_work_fulfillment
fulfillment_ssrs << ssr
end
end
end

#assigning arms..."
sub_protocol.arms.each do |arm|
check_arm_names(arm, master_protocol)
arm.protocol_id = master_protocol.id
arm.save(validate: false)
end

#assigning documents..."
sub_protocol.documents.each do |document|
document.protocol_id = master_protocol.id
document.save(validate: false)
end

#assigning_notes
sub_protocol.notes.each do |note|
note.notable_id = master_protocol.id
note.save(validate: false)
end

#delete sub protocol
sub_protocol.delete

#cleanup
merge_srs.perform_sr_merge
fix_ssr_ids.perform_id_fix
end
flash[:success] = 'Protocol merge successful'
end
end

private

def role_should_be_assigned?(role_to_be_assigned, protocol)
protocol.project_roles.each do |role|
if (role.role == role_to_be_assigned.role) && (role.identity_id == role_to_be_assigned.identity_id)
return false
end
end
return true
end

def authorize_overlord
unless @user.catalog_overlord?
render partial: 'service_requests/authorization_error',
locals: { error: 'You do not have access to perform a Protocol Merge',
in_dashboard: false
}
end
end

def has_research?(protocol, research_type)
protocol.research_types_info.try(research_type) || false
end
end
42 changes: 42 additions & 0 deletions app/lib/dashboard/fix_ssr_ids.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Dashboard

class FixSsrIds

def initialize(protocol)
@protocol = protocol
end

def perform_id_fix
requests = @protocol.sub_service_requests.group_by(&:ssr_id)
if @protocol.sub_service_requests.present?
last_ssr_id = @protocol.sub_service_requests.sort_by(&:ssr_id).last.ssr_id.to_i
dup_requests_to_be_incremented = []

requests.each do |ssr_id, ssr_array|
if ssr_array.size > 1 # we have duplicate ssr_ids
ssr_array.each_with_index do |ssr, index|
if index > 0
dup_requests_to_be_incremented << ssr # place all requests with dup ids in an array to deal with later
end
end
end
end

if dup_requests_to_be_incremented.size > 0
dup_requests_to_be_incremented.each do |ssr|
ssr.ssr_id = "%04d" % (last_ssr_id + 1)
ssr.save(validate: false)
last_ssr_id += 1
end
end

# we need to increment the protocol's next_ssr_id if we had some duplicates
new_last_ssr_id = @protocol.sub_service_requests.sort_by(&:ssr_id).last.ssr_id.to_i
if @protocol.next_ssr_id? && (@protocol.next_ssr_id <= new_last_ssr_id)
@protocol.next_ssr_id = new_last_ssr_id + 1
@protocol.save(validate: false)
end
end
end
end
end
100 changes: 100 additions & 0 deletions app/lib/dashboard/merge_srs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
module Dashboard

class MergeSrs

def perform_sr_merge
ServiceRequest.skip_callback(:save, :after, :set_original_submitted_date)

protocols = Protocol.joins(:service_requests).group('protocols.id').having('count(protocol_id) >= 2').to_a
protocols.each do |protocol|

# Merge remaining service requests into service request with most recently
# updated status
recent_service_request = protocol.service_requests.preload(:audits).distinct.map do |sr|
last_status_change = sr.audits.reverse.find { |a| a.audited_changes["status"] }
if last_status_change
[sr, last_status_change.created_at]
end
end.compact.sort do |l, r|
l[1] <=> r[1]
end

recent_service_request = if recent_service_request.empty?
# If audit trail runs out, use most recently updated ServiceRequest
protocol.service_requests.order(:updated_at).last
else
recent_service_request.last[0]
end

# And the latest submitted_at date
latest_submitted_at = protocol.service_requests.
order(:submitted_at).
where.not(submitted_at: nil).
pluck(:submitted_at).
last

if latest_submitted_at
recent_service_request.submitted_at = latest_submitted_at
recent_service_request.audit_comment = 'merge_srs'
recent_service_request.save(validate: false)
end

# Keep the earliest original_submitted_date among service requests
earliest_original_submitted_date = protocol.service_requests.
order(:original_submitted_date).
where.not(original_submitted_date: nil).
pluck(:original_submitted_date).
first

if earliest_original_submitted_date
recent_service_request.original_submitted_date = earliest_original_submitted_date
recent_service_request.audit_comment = 'merge_srs'
recent_service_request.save(validate: false)
end

# Move all SSR's, LineItems and ServiceRequest Notes under this recent_service_request
protocol.sub_service_requests.where.not(service_request_id: recent_service_request.id).each do |ssr|
ssr.service_request_id = recent_service_request.id
ssr.audit_comment = 'merge_srs'
ssr.save(validate: false)
end
line_items = LineItem.joins(:service_request).where(service_requests: { protocol_id: protocol.id }).where.not(service_request_id: recent_service_request.id)
line_items.each do |li|
li.service_request_id = recent_service_request.id
li.audit_comment = 'merge_srs'
li.save(validate: false)
end

protocol.service_requests.where.not(id: recent_service_request.id).each do |sr|
sr.notes.each do |note|
note.notable_id = recent_service_request.id
note.audit_comment = 'merge_srs'
note.save(validate: false)
end
end

delete_empty_srs(protocol)
end
end

private

def delete_empty_srs(protocol)
protocol.service_requests.includes(:sub_service_requests).where(sub_service_requests: { id: nil }).each do |sr|
begin
sr.destroy
sr.audits.last.update(comment: 'merge_srs')
rescue
# Probably a funky Note body...
sr.notes.each do |note|
note.notable_id = nil
note.audit_comment = 'merge_srs'
note.save(validate: false)
end
sr.reload.destroy
sr.audits.last.update(comment: 'merge_srs')
end
end
end
end
end
19 changes: 19 additions & 0 deletions app/views/dashboard/protocol_merges/_merge_tool.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#protocol-merges-container
%h1{style: 'text-align:center;'}
Protocol Merge Tool
%p &nbsp
%p &nbsp
%p &nbsp
%p
= label_tag 'master_protocol', 'Enter the id of the Master Protocol:'
= text_field_tag 'master_protocol', nil, style: 'margin-left:207px'
%p &nbsp
%p &nbsp
%p
= label_tag 'sub_protocol', 'Enter the id of the Protocol to be merged into the Master Protocol:'
= text_field_tag 'sub_protocol'
%p &nbsp
%p &nbsp
%p{style: 'text-align:center;'}
%button.btn.btn-info.navbar-btn#merge-button{ type: 'button', title: "Perform Protocol Merge"}
= "Perform Protocol Merge"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$("#flashes_container").html("<%= j render 'shared/flash' %>")
26 changes: 26 additions & 0 deletions app/views/dashboard/protocol_merges/show.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-# Copyright © 2011-2018 MUSC Foundation for Research Development
-# All rights reserved.
-# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials provided with the distribution.
-# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
-# derived from this software without specific prior written permission.
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= render 'dashboard/protocol_merges/merge_tool.html.haml'

:javascript
$(document).ready( function() {
Sparc.protocol.ready();
})
2 changes: 2 additions & 0 deletions app/views/layouts/dashboard/_dashboard_header.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
%li
%button.btn.btn-info.navbar-btn#epic-queue-btn{ type: 'button', title: t(:dashboard)[:navbar][:tooltips][:epic_queue], data: { toggle: 'tooltip', placement: 'bottom', delay: '{"show":"500"}'} }
= t(:dashboard)[:navbar][:epic_queue]
- if current_user.catalog_overlord?
= link_to "Protocol Merge", dashboard_protocol_merge_path, :class => ' header_button btn btn-primary btn-md', style: 'margin-top:7px;'
- if @show_messages
%li
%button.btn.btn-danger.navbar-btn#messages-btn{ type: 'button', title: t(:dashboard)[:navbar][:tooltips][:messages], data: { toggle: 'tooltip', placement: 'bottom', delay: '{"show":"500"}'} }
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@
resources :epic_queues, only: [:index, :destroy]
resources :epic_queue_records, only: [:index]

resource :protocol_merge do
get :perform_protocol_merge
end

resources :fulfillments

resources :line_items do
Expand Down

0 comments on commit 0bf8ed9

Please sign in to comment.