mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-30 08:54:31 +08:00
Merge pull request #32 from ZmagoD/zd_SCI_31
Fixing modules after integrating experiment-level
This commit is contained in:
commit
b13747291e
9 changed files with 563 additions and 503 deletions
|
@ -122,7 +122,9 @@ module PermissionHelper
|
||||||
] do |proxy, *args, &block|
|
] do |proxy, *args, &block|
|
||||||
if args[0]
|
if args[0]
|
||||||
my_module = args[0]
|
my_module = args[0]
|
||||||
if my_module.active? and my_module.project.active?
|
if my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active?
|
||||||
proxy.call(*args, &block)
|
proxy.call(*args, &block)
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
@ -334,11 +336,11 @@ module PermissionHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module(my_module)
|
def can_view_module(my_module)
|
||||||
can_view_project(my_module.project)
|
can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_edit_module(my_module)
|
def can_edit_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_archive_module(my_module)
|
def can_archive_module(my_module)
|
||||||
|
@ -350,7 +352,7 @@ module PermissionHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_edit_tags_for_module(my_module)
|
def can_edit_tags_for_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_add_tag_to_module(my_module)
|
def can_add_tag_to_module(my_module)
|
||||||
|
@ -362,23 +364,23 @@ module PermissionHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_info(my_module)
|
def can_view_module_info(my_module)
|
||||||
can_view_project(my_module.project)
|
can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_users(my_module)
|
def can_view_module_users(my_module)
|
||||||
can_view_project(my_module.project)
|
can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_edit_users_on_module(my_module)
|
def can_edit_users_on_module(my_module)
|
||||||
is_owner_of_project(my_module.project)
|
is_owner_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_add_user_to_module(my_module)
|
def can_add_user_to_module(my_module)
|
||||||
is_owner_of_project(my_module.project)
|
is_owner_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_remove_user_from_module(my_module)
|
def can_remove_user_from_module(my_module)
|
||||||
is_owner_of_project(my_module.project)
|
is_owner_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_protocols(my_module)
|
def can_view_module_protocols(my_module)
|
||||||
|
@ -386,84 +388,84 @@ module PermissionHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_activities(my_module)
|
def can_view_module_activities(my_module)
|
||||||
is_member_of_project(my_module.project)
|
is_member_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_comments(my_module)
|
def can_view_module_comments(my_module)
|
||||||
can_view_project(my_module.project)
|
can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_add_comment_to_module(my_module)
|
def can_add_comment_to_module(my_module)
|
||||||
is_technician_or_higher_of_project(my_module.project)
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_samples(my_module)
|
def can_view_module_samples(my_module)
|
||||||
can_view_module(my_module) and
|
can_view_module(my_module) and
|
||||||
can_view_samples(my_module.project.organization)
|
can_view_samples(my_module.experiment.project.organization)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_module_archive(my_module)
|
def can_view_module_archive(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---- RESULTS PERMISSIONS ----
|
# ---- RESULTS PERMISSIONS ----
|
||||||
|
|
||||||
def can_view_results_in_module(my_module)
|
def can_view_results_in_module(my_module)
|
||||||
can_view_project(my_module.project)
|
can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_or_download_result_assets(my_module)
|
def can_view_or_download_result_assets(my_module)
|
||||||
is_member_of_project(my_module.project) || can_view_project(my_module.project)
|
is_member_of_project(my_module.experiment.project) || can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_result_comments(my_module)
|
def can_view_result_comments(my_module)
|
||||||
can_view_project(my_module.project)
|
can_view_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_add_result_comment_in_module(my_module)
|
def can_add_result_comment_in_module(my_module)
|
||||||
is_technician_or_higher_of_project(my_module.project)
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---- RESULT TEXT PERMISSIONS ----
|
# ---- RESULT TEXT PERMISSIONS ----
|
||||||
|
|
||||||
def can_create_result_text_in_module(my_module)
|
def can_create_result_text_in_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_edit_result_text_in_module(my_module)
|
def can_edit_result_text_in_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_archive_result_text_in_module(my_module)
|
def can_archive_result_text_in_module(my_module)
|
||||||
is_owner_of_project(my_module.project)
|
is_owner_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---- RESULT TABLE PERMISSIONS ----
|
# ---- RESULT TABLE PERMISSIONS ----
|
||||||
|
|
||||||
def can_create_result_table_in_module(my_module)
|
def can_create_result_table_in_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_edit_result_table_in_module(my_module)
|
def can_edit_result_table_in_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_archive_result_table_in_module(my_module)
|
def can_archive_result_table_in_module(my_module)
|
||||||
is_owner_of_project(my_module.project)
|
is_owner_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---- RESULT ASSET PERMISSIONS ----
|
# ---- RESULT ASSET PERMISSIONS ----
|
||||||
|
|
||||||
def can_create_result_asset_in_module(my_module)
|
def can_create_result_asset_in_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_edit_result_asset_in_module(my_module)
|
def can_edit_result_asset_in_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_archive_result_asset_in_module(my_module)
|
def can_archive_result_asset_in_module(my_module)
|
||||||
is_owner_of_project(my_module.project)
|
is_owner_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---- REPORTS PERMISSIONS ----
|
# ---- REPORTS PERMISSIONS ----
|
||||||
|
@ -507,11 +509,11 @@ module PermissionHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_add_samples_to_module(my_module)
|
def can_add_samples_to_module(my_module)
|
||||||
is_technician_or_higher_of_project(my_module.project)
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_delete_samples_from_module(my_module)
|
def can_delete_samples_from_module(my_module)
|
||||||
is_technician_or_higher_of_project(my_module.project)
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ---- SAMPLE TYPES PERMISSIONS ----
|
# ---- SAMPLE TYPES PERMISSIONS ----
|
||||||
|
@ -562,7 +564,10 @@ module PermissionHelper
|
||||||
protocol.added_by == current_user
|
protocol.added_by == current_user
|
||||||
elsif protocol.in_module?
|
elsif protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and can_view_module(my_module)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
can_view_module(my_module) &&
|
||||||
|
my_module.experiment.active?
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -610,7 +615,10 @@ module PermissionHelper
|
||||||
def can_unlink_protocol(protocol)
|
def can_unlink_protocol(protocol)
|
||||||
if protocol.linked?
|
if protocol.linked?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.project.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.project) &&
|
||||||
|
my_module.experiment.active?
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -619,7 +627,10 @@ module PermissionHelper
|
||||||
def can_revert_protocol(protocol)
|
def can_revert_protocol(protocol)
|
||||||
if protocol.linked?
|
if protocol.linked?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project) &&
|
||||||
|
my_module.experiment.active?
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -628,7 +639,10 @@ module PermissionHelper
|
||||||
def can_update_protocol_from_parent(protocol)
|
def can_update_protocol_from_parent(protocol)
|
||||||
if protocol.linked?
|
if protocol.linked?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project) &&
|
||||||
|
my_module.experiment.active?
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -637,7 +651,10 @@ module PermissionHelper
|
||||||
def can_load_protocol_from_repository(protocol, source)
|
def can_load_protocol_from_repository(protocol, source)
|
||||||
if can_view_protocol(source)
|
if can_view_protocol(source)
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project) &&
|
||||||
|
my_module.experiment.active?
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -648,11 +665,12 @@ module PermissionHelper
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
parent = protocol.parent
|
parent = protocol.parent
|
||||||
|
|
||||||
my_module.active? and
|
my_module.active? &&
|
||||||
my_module.project.active? and
|
my_module.experiment.project.active? &&
|
||||||
is_normal_user_or_admin_of_organization(parent.organization) and
|
my_module.experiment.active? &&
|
||||||
is_user_or_higher_of_project(my_module.project) and
|
is_normal_user_or_admin_of_organization(parent.organization) &&
|
||||||
(parent.in_repository_public? or parent.in_repository_private?) and
|
is_user_or_higher_of_project(my_module.experiment.project) &&
|
||||||
|
(parent.in_repository_public? or parent.in_repository_private?) &&
|
||||||
parent.added_by == current_user
|
parent.added_by == current_user
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
@ -662,7 +680,7 @@ module PermissionHelper
|
||||||
# ---- STEPS PERMISSIONS ----
|
# ---- STEPS PERMISSIONS ----
|
||||||
|
|
||||||
def can_load_protocol_into_module(my_module)
|
def can_load_protocol_into_module(my_module)
|
||||||
is_user_or_higher_of_project(my_module.project)
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_export_protocol_from_module(my_module)
|
def can_export_protocol_from_module(my_module)
|
||||||
|
@ -670,18 +688,21 @@ module PermissionHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_copy_protocol_to_repository(my_module)
|
def can_copy_protocol_to_repository(my_module)
|
||||||
is_normal_user_or_admin_of_organization(my_module.project.organization)
|
is_normal_user_or_admin_of_organization(my_module.experiment.project.organization)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_link_copied_protocol_in_repository(protocol)
|
def can_link_copied_protocol_in_repository(protocol)
|
||||||
can_copy_protocol_to_repository(protocol.my_module) and
|
can_copy_protocol_to_repository(protocol.my_module) and
|
||||||
is_user_or_higher_of_project(protocol.my_module.project)
|
is_user_or_higher_of_project(protocol.my_module.experiment.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_view_steps_in_protocol(protocol)
|
def can_view_steps_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and can_view_module(my_module)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
can_view_module(my_module)
|
||||||
elsif protocol.in_repository?
|
elsif protocol.in_repository?
|
||||||
protocol.in_repository_active? and can_view_protocol(protocol)
|
protocol.in_repository_active? and can_view_protocol(protocol)
|
||||||
else
|
else
|
||||||
|
@ -692,7 +713,10 @@ module PermissionHelper
|
||||||
def can_create_step_in_protocol(protocol)
|
def can_create_step_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
elsif protocol.in_repository?
|
elsif protocol.in_repository?
|
||||||
protocol.in_repository_active? and can_edit_protocol(protocol)
|
protocol.in_repository_active? and can_edit_protocol(protocol)
|
||||||
else
|
else
|
||||||
|
@ -703,7 +727,10 @@ module PermissionHelper
|
||||||
def can_reorder_step_in_protocol(protocol)
|
def can_reorder_step_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
elsif protocol.in_repository?
|
elsif protocol.in_repository?
|
||||||
protocol.in_repository_active? and can_edit_protocol(protocol)
|
protocol.in_repository_active? and can_edit_protocol(protocol)
|
||||||
else
|
else
|
||||||
|
@ -720,7 +747,10 @@ module PermissionHelper
|
||||||
def can_edit_step_in_protocol(protocol)
|
def can_edit_step_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
elsif protocol.in_repository?
|
elsif protocol.in_repository?
|
||||||
protocol.in_repository_active? and can_edit_protocol(protocol)
|
protocol.in_repository_active? and can_edit_protocol(protocol)
|
||||||
else
|
else
|
||||||
|
@ -731,7 +761,10 @@ module PermissionHelper
|
||||||
def can_delete_step_in_protocol(protocol)
|
def can_delete_step_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_owner_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_owner_of_project(my_module.experiment.project)
|
||||||
elsif protocol.in_repository?
|
elsif protocol.in_repository?
|
||||||
protocol.in_repository_active? and can_edit_protocol(protocol)
|
protocol.in_repository_active? and can_edit_protocol(protocol)
|
||||||
else
|
else
|
||||||
|
@ -742,7 +775,10 @@ module PermissionHelper
|
||||||
def can_view_step_comments(protocol)
|
def can_view_step_comments(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and can_view_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
can_view_project(my_module.experiment.project)
|
||||||
else
|
else
|
||||||
# In repository, comments are disabled
|
# In repository, comments are disabled
|
||||||
false
|
false
|
||||||
|
@ -752,7 +788,10 @@ module PermissionHelper
|
||||||
def can_add_step_comment_in_protocol(protocol)
|
def can_add_step_comment_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_technician_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
else
|
else
|
||||||
# In repository, user cannot complete steps
|
# In repository, user cannot complete steps
|
||||||
false
|
false
|
||||||
|
@ -762,8 +801,10 @@ module PermissionHelper
|
||||||
def can_view_or_download_step_assets(protocol)
|
def can_view_or_download_step_assets(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and
|
my_module.active? &&
|
||||||
(is_member_of_project(my_module.project) || can_view_project(my_module.project))
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
(is_member_of_project(my_module.experiment.project) || can_view_project(my_module.experiment.project))
|
||||||
elsif protocol.in_repository?
|
elsif protocol.in_repository?
|
||||||
protocol.in_repository_active? and can_view_protocol(protocol)
|
protocol.in_repository_active? and can_view_protocol(protocol)
|
||||||
else
|
else
|
||||||
|
@ -774,7 +815,10 @@ module PermissionHelper
|
||||||
def can_complete_step_in_protocol(protocol)
|
def can_complete_step_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_technician_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
else
|
else
|
||||||
# In repository, user cannot complete steps
|
# In repository, user cannot complete steps
|
||||||
false
|
false
|
||||||
|
@ -784,7 +828,10 @@ module PermissionHelper
|
||||||
def can_uncomplete_step_in_protocol(protocol)
|
def can_uncomplete_step_in_protocol(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
else
|
else
|
||||||
# In repository, user cannot complete steps
|
# In repository, user cannot complete steps
|
||||||
false
|
false
|
||||||
|
@ -794,7 +841,10 @@ module PermissionHelper
|
||||||
def can_check_checkbox(protocol)
|
def can_check_checkbox(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_technician_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_technician_or_higher_of_project(my_module.experiment.project)
|
||||||
else
|
else
|
||||||
# In repository, user cannot check checkboxes
|
# In repository, user cannot check checkboxes
|
||||||
false
|
false
|
||||||
|
@ -804,7 +854,10 @@ module PermissionHelper
|
||||||
def can_uncheck_checkbox(protocol)
|
def can_uncheck_checkbox(protocol)
|
||||||
if protocol.in_module?
|
if protocol.in_module?
|
||||||
my_module = protocol.my_module
|
my_module = protocol.my_module
|
||||||
my_module.active? and my_module.project.active? and is_user_or_higher_of_project(my_module.project)
|
my_module.active? &&
|
||||||
|
my_module.experiment.project.active? &&
|
||||||
|
my_module.experiment.active? &&
|
||||||
|
is_user_or_higher_of_project(my_module.experiment.project)
|
||||||
else
|
else
|
||||||
# In repository, user cannot check checkboxes
|
# In repository, user cannot check checkboxes
|
||||||
false
|
false
|
||||||
|
|
|
@ -22,4 +22,440 @@ class Experiment < ActiveRecord::Base
|
||||||
experiment.validates :archived_by, presence: true
|
experiment.validates :archived_by, presence: true
|
||||||
experiment.validates :archived_on, presence: true
|
experiment.validates :archived_on, presence: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def modules_without_group
|
||||||
|
MyModule.where(experiment_id: id).where(my_module_group: nil)
|
||||||
|
.where(archived: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def active_module_groups
|
||||||
|
self.my_module_groups.joins(:my_modules)
|
||||||
|
.where('my_modules.archived = ?', false)
|
||||||
|
.distinct
|
||||||
|
end
|
||||||
|
|
||||||
|
def assigned_modules(user)
|
||||||
|
role = self.project.user_role(user)
|
||||||
|
if role.blank?
|
||||||
|
return MyModule.none
|
||||||
|
elsif role == "owner"
|
||||||
|
return self.active_modules
|
||||||
|
else
|
||||||
|
return self.active_modules
|
||||||
|
.joins(:user_my_modules)
|
||||||
|
.where("user_my_modules.user_id IN (?)", user.id)
|
||||||
|
.distinct
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def active_modules
|
||||||
|
my_modules.where(:archived => false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def archived_modules
|
||||||
|
my_modules.where(:archived => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assigned_samples
|
||||||
|
Sample.joins(:my_modules).where(my_modules: {id: my_modules} )
|
||||||
|
end
|
||||||
|
|
||||||
|
def unassigned_samples(assigned_samples)
|
||||||
|
Sample.where(organization_id: organization).where.not(id: assigned_samples)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_canvas(
|
||||||
|
to_archive,
|
||||||
|
to_add,
|
||||||
|
to_rename,
|
||||||
|
to_clone,
|
||||||
|
connections,
|
||||||
|
positions,
|
||||||
|
current_user,
|
||||||
|
module_groups
|
||||||
|
)
|
||||||
|
cloned_modules = []
|
||||||
|
begin
|
||||||
|
Experiment.transaction do
|
||||||
|
# First, add new modules
|
||||||
|
new_ids, cloned_pairs, originals = add_modules(
|
||||||
|
to_add, to_clone, current_user)
|
||||||
|
cloned_modules = cloned_pairs.collect { |mn, _| mn }
|
||||||
|
|
||||||
|
# Rename modules
|
||||||
|
rename_modules(to_rename)
|
||||||
|
|
||||||
|
# Add activities that modules were created
|
||||||
|
originals.each do |m|
|
||||||
|
Activity.create(
|
||||||
|
type_of: :create_module,
|
||||||
|
user: current_user,
|
||||||
|
project: selfproject,
|
||||||
|
my_module: m,
|
||||||
|
message: I18n.t(
|
||||||
|
"activities.create_module",
|
||||||
|
user: current_user.full_name,
|
||||||
|
module: m.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add activities that modules were cloned
|
||||||
|
cloned_pairs.each do |mn, mo|
|
||||||
|
Activity.create(
|
||||||
|
type_of: :clone_module,
|
||||||
|
project: mn.experiment.project,
|
||||||
|
my_module: mn,
|
||||||
|
user: current_user,
|
||||||
|
message: I18n.t(
|
||||||
|
"activities.clone_module",
|
||||||
|
user: current_user.full_name,
|
||||||
|
module_new: mn.name,
|
||||||
|
module_original: mo.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Then, archive modules that need to be archived
|
||||||
|
archive_modules(to_archive, current_user)
|
||||||
|
|
||||||
|
# Update connections, positions & module group variables
|
||||||
|
# with actual IDs retrieved from the new modules creation
|
||||||
|
updated_connections = []
|
||||||
|
connections.each do |a,b|
|
||||||
|
updated_connections << [new_ids.fetch(a, a), new_ids.fetch(b, b)]
|
||||||
|
end
|
||||||
|
updated_positions = Hash.new
|
||||||
|
positions.each do |id, pos|
|
||||||
|
updated_positions[new_ids.fetch(id, id)] = pos
|
||||||
|
end
|
||||||
|
updated_module_groups = {}
|
||||||
|
module_groups.each do |id, name|
|
||||||
|
updated_module_groups[new_ids.fetch(id, id)] = name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update connections
|
||||||
|
update_module_connections(updated_connections)
|
||||||
|
|
||||||
|
# Update module positions (no validation needed here)
|
||||||
|
update_module_positions(updated_positions)
|
||||||
|
|
||||||
|
# Normalize module positions
|
||||||
|
normalize_module_positions
|
||||||
|
|
||||||
|
# Finally, update module groups
|
||||||
|
update_module_groups(updated_module_groups, current_user)
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::ActiveRecordError, ArgumentError, ActiveRecord::RecordNotSaved
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Archive all modules. Receives an array of module integer IDs.
|
||||||
|
def archive_modules(module_ids)
|
||||||
|
module_ids.each do |m_id|
|
||||||
|
my_module = self.my_modules.find_by_id(m_id)
|
||||||
|
unless my_module.blank?
|
||||||
|
my_module.archive!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
modules.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
# Archive all modules. Receives an array of module integer IDs and current user.
|
||||||
|
def archive_modules(module_ids, current_user)
|
||||||
|
module_ids.each do |m_id|
|
||||||
|
my_module = self.my_modules.find_by_id(m_id)
|
||||||
|
unless my_module.blank?
|
||||||
|
my_module.archive!(current_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
my_modules.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add modules, and returns a map of "virtual" IDs with
|
||||||
|
# actual IDs of saved modules.
|
||||||
|
# to_add is an array of hashes, each containing 'name',
|
||||||
|
# 'x', 'y' and 'id'.
|
||||||
|
# to_clone is a hash, storing new cloned modules as keys,
|
||||||
|
# and original modules as values.
|
||||||
|
def add_modules(to_add, to_clone, current_user)
|
||||||
|
originals = []
|
||||||
|
cloned_pairs = {}
|
||||||
|
ids_map = Hash.new
|
||||||
|
to_add.each do |m|
|
||||||
|
original = MyModule.find_by_id(to_clone.fetch(m[:id], nil))
|
||||||
|
if original.present? then
|
||||||
|
my_module = original.deep_clone(current_user)
|
||||||
|
cloned_pairs[my_module] = original
|
||||||
|
else
|
||||||
|
my_module = MyModule.new(
|
||||||
|
experiment: self)
|
||||||
|
originals << my_module
|
||||||
|
end
|
||||||
|
|
||||||
|
my_module.name = m[:name]
|
||||||
|
my_module.x = m[:x]
|
||||||
|
my_module.y = m[:y]
|
||||||
|
my_module.created_by = current_user
|
||||||
|
my_module.last_modified_by = current_user
|
||||||
|
my_module.save!
|
||||||
|
|
||||||
|
ids_map[m[:id]] = my_module.id.to_s
|
||||||
|
end
|
||||||
|
my_modules.reload
|
||||||
|
return ids_map, cloned_pairs, originals
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rename modules; this method accepts a map where keys
|
||||||
|
# represent IDs of modules, and values new names for
|
||||||
|
# such modules. If a module with given ID doesn't exist,
|
||||||
|
# it's obviously not updated.
|
||||||
|
def rename_modules(to_rename)
|
||||||
|
to_rename.each do |id, new_name|
|
||||||
|
my_module = MyModule.find_by_id(id)
|
||||||
|
if my_module.present?
|
||||||
|
my_module.name = new_name
|
||||||
|
my_module.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update connections for all modules in this project.
|
||||||
|
# Input is an array of arrays, where first element represents
|
||||||
|
# source node, and second element represents target node.
|
||||||
|
# Example input: [ [1, 2], [2, 3], [4, 5], [2, 5] ]
|
||||||
|
def update_module_connections(connections)
|
||||||
|
require 'rgl/base'
|
||||||
|
require 'rgl/adjacency'
|
||||||
|
require 'rgl/topsort'
|
||||||
|
|
||||||
|
dg = RGL::DirectedAdjacencyGraph.new
|
||||||
|
connections.each do |a,b|
|
||||||
|
# Check if both vertices exist
|
||||||
|
if (my_modules.find_all {|m| [a.to_i, b.to_i].include? m.id }).count == 2
|
||||||
|
dg.add_edge(a, b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if cycles exist!
|
||||||
|
topsort = dg.topsort_iterator.to_a
|
||||||
|
if topsort.length == 0 and dg.edges.size > 1
|
||||||
|
raise ArgumentError, "Cycles exist."
|
||||||
|
end
|
||||||
|
|
||||||
|
# First, delete existing connections
|
||||||
|
# but keep a copy of previous state
|
||||||
|
previous_sources = {}
|
||||||
|
previous_sources.default = []
|
||||||
|
my_modules.each do |m|
|
||||||
|
previous_sources[m.id] = []
|
||||||
|
m.inputs.each do |c|
|
||||||
|
previous_sources[m.id] << c.from
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.my_modules.each do |m|
|
||||||
|
unless m.outputs.destroy_all
|
||||||
|
raise ActiveRecord::ActiveRecordError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Add new connections
|
||||||
|
filtered_edges = dg.edges.collect { |e| [e.source, e.target] }
|
||||||
|
filtered_edges.each do |a, b|
|
||||||
|
Connection.create!(:input_id => b, :output_id => a)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unassign samples from former downstream modules
|
||||||
|
# for all destroyed connections
|
||||||
|
unassign_samples_from_old_downstream_modules(previous_sources)
|
||||||
|
|
||||||
|
visited = []
|
||||||
|
# Assign samples to all new downstream modules
|
||||||
|
filtered_edges.each do |a, b|
|
||||||
|
source = self.my_modules.find(a.to_i)
|
||||||
|
target = self.my_modules.find(b.to_i)
|
||||||
|
# Do this only for new edges
|
||||||
|
if previous_sources[target.id].exclude?(source)
|
||||||
|
# Go as high upstream as new edges take us
|
||||||
|
# and then assign samples to all downsteam samples
|
||||||
|
assign_samples_to_new_downstream_modules(previous_sources, visited, source)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Save topological order of modules (for modules without workflow,
|
||||||
|
# leave them unordered)
|
||||||
|
self.my_modules.each do |m|
|
||||||
|
if topsort.include? m.id.to_s
|
||||||
|
m.workflow_order = topsort.find_index(m.id.to_s)
|
||||||
|
else
|
||||||
|
m.workflow_order = -1
|
||||||
|
end
|
||||||
|
m.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure to reload my modules, which now have updated connections and samples
|
||||||
|
self.my_modules.reload
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# When connections are deleted, unassign samples that
|
||||||
|
# are not inherited anymore
|
||||||
|
def unassign_samples_from_old_downstream_modules(sources)
|
||||||
|
self.my_modules.each do |my_module|
|
||||||
|
sources[my_module.id].each do |s|
|
||||||
|
# Only do this for newly deleted connections
|
||||||
|
if s.outputs.map{|i| i.to}.exclude? my_module
|
||||||
|
my_module.get_downstream_modules.each do |dm|
|
||||||
|
# Get unique samples for all upstream modules
|
||||||
|
um = dm.get_upstream_modules
|
||||||
|
um.shift # remove current module
|
||||||
|
ums = um.map{|m| m.samples}.flatten.uniq
|
||||||
|
s.samples.each do |sample|
|
||||||
|
dm.samples.delete(sample) if ums.exclude? sample
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Assign samples to new connections recursively
|
||||||
|
def assign_samples_to_new_downstream_modules(sources, visited, my_module)
|
||||||
|
# If samples are already assigned for this module, stop going upstream
|
||||||
|
if visited.include? (my_module)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
visited << my_module
|
||||||
|
# Edge case, when module is source or it doesn't have any new input connections
|
||||||
|
if my_module.inputs.blank? or (
|
||||||
|
my_module.inputs.map{|c| c.from} -
|
||||||
|
sources[my_module.id]
|
||||||
|
).empty?
|
||||||
|
my_module.get_downstream_modules.each do |dm|
|
||||||
|
new_samples = my_module.samples.select { |el| dm.samples.exclude?(el) }
|
||||||
|
dm.samples.push(*new_samples)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
my_module.inputs.each do |input|
|
||||||
|
# Go upstream for new in connections
|
||||||
|
if sources[my_module.id].exclude?(input.from)
|
||||||
|
assign_samples_to_new_downstream_modules(input.from)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Updates positions of modules.
|
||||||
|
# Input is a map where keys are module IDs, and values are
|
||||||
|
# hashes like { x: <x>, y: <y> }.
|
||||||
|
def update_module_positions(positions)
|
||||||
|
positions.each do |id, pos|
|
||||||
|
unless MyModule.update(id, x: pos[:x], y: pos[:y])
|
||||||
|
raise ActiveRecord::ActiveRecordError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.my_modules.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
# Normalize module positions in this project.
|
||||||
|
def normalize_module_positions
|
||||||
|
# This method normalizes module positions so x-s and y-s
|
||||||
|
# are all positive
|
||||||
|
x_diff = (self.my_modules.collect { |m| m.x }).min
|
||||||
|
y_diff = (self.my_modules.collect { |m| m.y }).min
|
||||||
|
|
||||||
|
self.my_modules.each do |m|
|
||||||
|
unless
|
||||||
|
m.update_attribute(:x, m.x - x_diff) and
|
||||||
|
m.update_attribute(:y, m.y - y_diff)
|
||||||
|
raise ActiveRecord::ActiveRecordError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Recalculate module groups in this project. Input is
|
||||||
|
# a hash of module ids and their corresponding module names.
|
||||||
|
def update_module_groups(module_groups, current_user)
|
||||||
|
require 'rgl/base'
|
||||||
|
require 'rgl/adjacency'
|
||||||
|
require 'rgl/connected_components'
|
||||||
|
|
||||||
|
dg = RGL::DirectedAdjacencyGraph[]
|
||||||
|
group_ids = Set.new
|
||||||
|
active_modules.each do |m|
|
||||||
|
unless m.my_module_group.blank?
|
||||||
|
group_ids << m.my_module_group.id
|
||||||
|
end
|
||||||
|
unless dg.has_vertex? m.id
|
||||||
|
dg.add_vertex m.id
|
||||||
|
end
|
||||||
|
m.outputs.each do |o|
|
||||||
|
dg.add_edge m.id, o.to.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
workflows = []
|
||||||
|
dg.to_undirected.each_connected_component { |w| workflows << w }
|
||||||
|
|
||||||
|
# Retrieve maximum allowed module group name
|
||||||
|
max_length = (MyModuleGroup.validators_on(:name).select { |v| v.class == ActiveModel::Validations::LengthValidator }).first.options[:maximum]
|
||||||
|
# For each workflow, generate new names
|
||||||
|
new_index = 1
|
||||||
|
wf_names = []
|
||||||
|
suffix = I18n.t("my_module_groups.new.suffix")
|
||||||
|
cut_index = -(suffix.length + 1)
|
||||||
|
workflows.each do |w|
|
||||||
|
modules = MyModule.find(w)
|
||||||
|
|
||||||
|
# Get an array of module names
|
||||||
|
names = []
|
||||||
|
modules.each do |m|
|
||||||
|
names << module_groups.fetch(m.id.to_s, "")
|
||||||
|
end
|
||||||
|
names = names.uniq
|
||||||
|
name = (names.select { |v| v != "" }).join(", ")
|
||||||
|
|
||||||
|
if w.length <= 1
|
||||||
|
name = nil
|
||||||
|
elsif name.blank?
|
||||||
|
name = I18n.t("my_module_groups.new.name", index: new_index)
|
||||||
|
new_index += 1
|
||||||
|
while MyModuleGroup.find_by(name: name).present?
|
||||||
|
name = I18n.t("my_module_groups.new.name", index: new_index)
|
||||||
|
new_index += 1
|
||||||
|
end
|
||||||
|
elsif name.length > max_length
|
||||||
|
# If length is too long, shorten it
|
||||||
|
name = name[0..(max_length + cut_index)] + suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
wf_names << name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove any existing module groups from modules
|
||||||
|
unless MyModuleGroup.destroy_all(:id => group_ids.to_a)
|
||||||
|
raise ActiveRecord::ActiveRecordError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Second, create new groups
|
||||||
|
workflows.each_with_index do |w, i|
|
||||||
|
# Single modules are not considered part of any workflow
|
||||||
|
if w.length > 1
|
||||||
|
group = MyModuleGroup.new(
|
||||||
|
name: wf_names[i],
|
||||||
|
project: self,
|
||||||
|
my_modules: MyModule.find(w))
|
||||||
|
group.created_by = current_user
|
||||||
|
group.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
my_module_groups.reload
|
||||||
|
true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -117,20 +117,22 @@ class MyModule < ActiveRecord::Base
|
||||||
User.find_by_sql(
|
User.find_by_sql(
|
||||||
"SELECT DISTINCT users.id, users.full_name FROM users " +
|
"SELECT DISTINCT users.id, users.full_name FROM users " +
|
||||||
"INNER JOIN user_projects ON users.id = user_projects.user_id " +
|
"INNER JOIN user_projects ON users.id = user_projects.user_id " +
|
||||||
"WHERE user_projects.project_id = #{project_id.to_s}" +
|
"INNER JOIN experiments ON experiments.project_id = user_projects.project_id " +
|
||||||
|
"WHERE experiments.id = #{experiment_id.to_s}" +
|
||||||
" AND users.id NOT IN " +
|
" AND users.id NOT IN " +
|
||||||
"(SELECT DISTINCT user_id FROM user_my_modules WHERE user_my_modules.my_module_id = #{id.to_s})"
|
"(SELECT DISTINCT user_id FROM user_my_modules WHERE user_my_modules.my_module_id = #{id.to_s})"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unassigned_samples
|
def unassigned_samples
|
||||||
Sample.where(organization_id: project.organization).where.not(id: samples)
|
Sample.where(organization_id: experiment.project.organization).where.not(id: samples)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unassigned_tags
|
def unassigned_tags
|
||||||
Tag.find_by_sql(
|
Tag.find_by_sql(
|
||||||
"SELECT DISTINCT tags.id, tags.name, tags.color FROM tags " +
|
"SELECT DISTINCT tags.id, tags.name, tags.color FROM tags " +
|
||||||
"WHERE tags.project_id = #{project_id.to_s} AND tags.id NOT IN " +
|
"INNER JOIN experiments ON experiments.project_id = tags.project_id " +
|
||||||
|
"WHERE experiments.id = #{experiment_id.to_s} AND tags.id NOT IN " +
|
||||||
"(SELECT DISTINCT tag_id FROM my_module_tags WHERE my_module_tags.my_module_id = #{id.to_s})"
|
"(SELECT DISTINCT tag_id FROM my_module_tags WHERE my_module_tags.my_module_id = #{id.to_s})"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -285,7 +287,7 @@ class MyModule < ActiveRecord::Base
|
||||||
# Copy the module
|
# Copy the module
|
||||||
clone = MyModule.new(
|
clone = MyModule.new(
|
||||||
name: self.name,
|
name: self.name,
|
||||||
project: self.project,
|
experiment: self.experiment,
|
||||||
description: self.description,
|
description: self.description,
|
||||||
x: self.x,
|
x: self.x,
|
||||||
y: self.y)
|
y: self.y)
|
||||||
|
@ -306,7 +308,7 @@ class MyModule < ActiveRecord::Base
|
||||||
# Writes to user log.
|
# Writes to user log.
|
||||||
def log(message)
|
def log(message)
|
||||||
final = "[%s] %s" % [name, message]
|
final = "[%s] %s" % [name, message]
|
||||||
project.log(final)
|
experiment.project.log(final)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -318,12 +320,12 @@ class MyModule < ActiveRecord::Base
|
||||||
# Find an empty position for the restored module. It's
|
# Find an empty position for the restored module. It's
|
||||||
# basically a first empty row with x=0.
|
# basically a first empty row with x=0.
|
||||||
def get_new_position
|
def get_new_position
|
||||||
if project.blank?
|
if experiment.blank?
|
||||||
return { x: 0, y: 0 }
|
return { x: 0, y: 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
new_y = 0
|
new_y = 0
|
||||||
positions = project.active_modules.collect{ |m| [m.x, m.y] }
|
positions = experiment.active_modules.collect{ |m| [m.x, m.y] }
|
||||||
(0..10000).each do |n|
|
(0..10000).each do |n|
|
||||||
unless positions.include? [0, n]
|
unless positions.include? [0, n]
|
||||||
new_y = n
|
new_y = n
|
||||||
|
|
|
@ -29,7 +29,8 @@ class MyModuleGroup < ActiveRecord::Base
|
||||||
|
|
||||||
new_query = MyModuleGroup
|
new_query = MyModuleGroup
|
||||||
.distinct
|
.distinct
|
||||||
.where("my_module_groups.project_id IN (?)", project_ids)
|
.joins(:experiment)
|
||||||
|
.where("experiment.project_id IN (?)", project_ids)
|
||||||
.where_attributes_like(:name, a_query)
|
.where_attributes_like(:name, a_query)
|
||||||
|
|
||||||
# Show all results if needed
|
# Show all results if needed
|
||||||
|
|
|
@ -257,7 +257,7 @@ class Organization < ActiveRecord::Base
|
||||||
projects.includes(
|
projects.includes(
|
||||||
my_modules: { protocols: { steps: :assets }, results: { result_asset: :asset } }
|
my_modules: { protocols: { steps: :assets }, results: { result_asset: :asset } }
|
||||||
).find_each do |project|
|
).find_each do |project|
|
||||||
project.my_modules.find_each do |my_module|
|
project.project_my_modules.find_each do |my_module|
|
||||||
my_module.protocol.steps.find_each do |step|
|
my_module.protocol.steps.find_each do |step|
|
||||||
step.assets.find_each { |asset| st += asset.estimated_size }
|
step.assets.find_each { |asset| st += asset.estimated_size }
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,6 @@ class Project < ActiveRecord::Base
|
||||||
|
|
||||||
def self.search(user, include_archived, query = nil, page = 1)
|
def self.search(user, include_archived, query = nil, page = 1)
|
||||||
|
|
||||||
|
|
||||||
if query
|
if query
|
||||||
a_query = query.strip
|
a_query = query.strip
|
||||||
.gsub("_","\\_")
|
.gsub("_","\\_")
|
||||||
|
@ -89,14 +88,6 @@ class Project < ActiveRecord::Base
|
||||||
.limit(per_page)
|
.limit(per_page)
|
||||||
end
|
end
|
||||||
|
|
||||||
def active_modules
|
|
||||||
self.my_modules.where(:archived => false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def archived_modules
|
|
||||||
self.my_modules.where(:archived => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unassigned_users
|
def unassigned_users
|
||||||
User
|
User
|
||||||
.joins("INNER JOIN user_organizations ON users.id = user_organizations.user_id ")
|
.joins("INNER JOIN user_organizations ON users.id = user_organizations.user_id ")
|
||||||
|
@ -105,20 +96,6 @@ class Project < ActiveRecord::Base
|
||||||
.where("users.id NOT IN (?)", UserProject.where(project: self).select(:id).distinct)
|
.where("users.id NOT IN (?)", UserProject.where(project: self).select(:id).distinct)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assigned_modules(user)
|
|
||||||
role = self.user_role(user)
|
|
||||||
if role.blank?
|
|
||||||
return MyModule.none
|
|
||||||
elsif role == "owner"
|
|
||||||
return self.my_modules.where(archived: false)
|
|
||||||
else
|
|
||||||
return self.my_modules.where(archived: false)
|
|
||||||
.joins(:user_my_modules)
|
|
||||||
.where("user_my_modules.user_id IN (?)", user.id)
|
|
||||||
.distinct
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_role(user)
|
def user_role(user)
|
||||||
unless self.users.include? user
|
unless self.users.include? user
|
||||||
return nil
|
return nil
|
||||||
|
@ -127,431 +104,22 @@ class Project < ActiveRecord::Base
|
||||||
return (self.user_projects.select { |up| up.user == user }).first.role
|
return (self.user_projects.select { |up| up.user == user }).first.role
|
||||||
end
|
end
|
||||||
|
|
||||||
def modules_without_group
|
def project_my_modules
|
||||||
MyModule.where(project_id: id).where(my_module_group: nil)
|
MyModule.where('"experiment_id" IN (?)', experiments.select(:id))
|
||||||
.where(archived: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def active_module_groups
|
|
||||||
self.my_module_groups.joins(:my_modules)
|
|
||||||
.where('my_modules.archived = ?', false)
|
|
||||||
.distinct
|
|
||||||
end
|
|
||||||
|
|
||||||
def assigned_samples
|
|
||||||
Sample.joins(:my_modules).where(my_modules: {id: my_modules} )
|
|
||||||
end
|
|
||||||
|
|
||||||
def unassigned_samples(assigned_samples)
|
|
||||||
Sample.where(organization_id: organization).where.not(id: assigned_samples)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def space_taken
|
def space_taken
|
||||||
st = 0
|
st = 0
|
||||||
my_modules.find_each do |my_module|
|
project_my_modules.find_each do |my_module|
|
||||||
st += my_module.space_taken
|
st += my_module.space_taken
|
||||||
end
|
end
|
||||||
st
|
st
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_canvas(
|
|
||||||
to_archive,
|
|
||||||
to_add,
|
|
||||||
to_rename,
|
|
||||||
to_clone,
|
|
||||||
connections,
|
|
||||||
positions,
|
|
||||||
current_user,
|
|
||||||
module_groups
|
|
||||||
)
|
|
||||||
cloned_modules = []
|
|
||||||
begin
|
|
||||||
Project.transaction do
|
|
||||||
# First, add new modules
|
|
||||||
new_ids, cloned_pairs, originals = add_modules(
|
|
||||||
to_add, to_clone, current_user)
|
|
||||||
cloned_modules = cloned_pairs.collect { |mn, _| mn }
|
|
||||||
|
|
||||||
# Rename modules
|
|
||||||
rename_modules(to_rename)
|
|
||||||
|
|
||||||
# Add activities that modules were created
|
|
||||||
originals.each do |m|
|
|
||||||
Activity.create(
|
|
||||||
type_of: :create_module,
|
|
||||||
user: current_user,
|
|
||||||
project: self,
|
|
||||||
my_module: m,
|
|
||||||
message: I18n.t(
|
|
||||||
"activities.create_module",
|
|
||||||
user: current_user.full_name,
|
|
||||||
module: m.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add activities that modules were cloned
|
|
||||||
cloned_pairs.each do |mn, mo|
|
|
||||||
Activity.create(
|
|
||||||
type_of: :clone_module,
|
|
||||||
project: mn.project,
|
|
||||||
my_module: mn,
|
|
||||||
user: current_user,
|
|
||||||
message: I18n.t(
|
|
||||||
"activities.clone_module",
|
|
||||||
user: current_user.full_name,
|
|
||||||
module_new: mn.name,
|
|
||||||
module_original: mo.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Then, archive modules that need to be archived
|
|
||||||
archive_modules(to_archive, current_user)
|
|
||||||
|
|
||||||
# Update connections, positions & module group variables
|
|
||||||
# with actual IDs retrieved from the new modules creation
|
|
||||||
updated_connections = []
|
|
||||||
connections.each do |a,b|
|
|
||||||
updated_connections << [new_ids.fetch(a, a), new_ids.fetch(b, b)]
|
|
||||||
end
|
|
||||||
updated_positions = Hash.new
|
|
||||||
positions.each do |id, pos|
|
|
||||||
updated_positions[new_ids.fetch(id, id)] = pos
|
|
||||||
end
|
|
||||||
updated_module_groups = {}
|
|
||||||
module_groups.each do |id, name|
|
|
||||||
updated_module_groups[new_ids.fetch(id, id)] = name
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update connections
|
|
||||||
update_module_connections(updated_connections)
|
|
||||||
|
|
||||||
# Update module positions (no validation needed here)
|
|
||||||
update_module_positions(updated_positions)
|
|
||||||
|
|
||||||
# Normalize module positions
|
|
||||||
normalize_module_positions
|
|
||||||
|
|
||||||
# Finally, update module groups
|
|
||||||
update_module_groups(updated_module_groups, current_user)
|
|
||||||
end
|
|
||||||
rescue ActiveRecord::ActiveRecordError, ArgumentError, ActiveRecord::RecordNotSaved
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Writes to user log.
|
# Writes to user log.
|
||||||
def log(message)
|
def log(message)
|
||||||
final = "[%s] %s" % [name, message]
|
final = "[%s] %s" % [name, message]
|
||||||
organization.log(final)
|
organization.log(final)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Archive all modules. Receives an array of module integer IDs.
|
|
||||||
def archive_modules(module_ids)
|
|
||||||
module_ids.each do |m_id|
|
|
||||||
my_module = my_modules.find_by_id(m_id)
|
|
||||||
unless my_module.blank?
|
|
||||||
my_module.archive!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
my_modules.reload
|
|
||||||
end
|
|
||||||
|
|
||||||
# Archive all modules. Receives an array of module integer IDs and current user.
|
|
||||||
def archive_modules(module_ids, current_user)
|
|
||||||
module_ids.each do |m_id|
|
|
||||||
my_module = my_modules.find_by_id(m_id)
|
|
||||||
unless my_module.blank?
|
|
||||||
my_module.archive!(current_user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
my_modules.reload
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add modules, and returns a map of "virtual" IDs with
|
|
||||||
# actual IDs of saved modules.
|
|
||||||
# to_add is an array of hashes, each containing 'name',
|
|
||||||
# 'x', 'y' and 'id'.
|
|
||||||
# to_clone is a hash, storing new cloned modules as keys,
|
|
||||||
# and original modules as values.
|
|
||||||
def add_modules(to_add, to_clone, current_user)
|
|
||||||
originals = []
|
|
||||||
cloned_pairs = {}
|
|
||||||
ids_map = Hash.new
|
|
||||||
to_add.each do |m|
|
|
||||||
original = MyModule.find_by_id(to_clone.fetch(m[:id], nil))
|
|
||||||
if original.present? then
|
|
||||||
my_module = original.deep_clone(current_user)
|
|
||||||
cloned_pairs[my_module] = original
|
|
||||||
else
|
|
||||||
my_module = MyModule.new(
|
|
||||||
project: self)
|
|
||||||
originals << my_module
|
|
||||||
end
|
|
||||||
|
|
||||||
my_module.name = m[:name]
|
|
||||||
my_module.x = m[:x]
|
|
||||||
my_module.y = m[:y]
|
|
||||||
my_module.created_by = current_user
|
|
||||||
my_module.last_modified_by = current_user
|
|
||||||
my_module.save!
|
|
||||||
|
|
||||||
ids_map[m[:id]] = my_module.id.to_s
|
|
||||||
end
|
|
||||||
my_modules.reload
|
|
||||||
return ids_map, cloned_pairs, originals
|
|
||||||
end
|
|
||||||
|
|
||||||
# Rename modules; this method accepts a map where keys
|
|
||||||
# represent IDs of modules, and values new names for
|
|
||||||
# such modules. If a module with given ID doesn't exist,
|
|
||||||
# it's obviously not updated.
|
|
||||||
def rename_modules(to_rename)
|
|
||||||
to_rename.each do |id, new_name|
|
|
||||||
my_module = MyModule.find_by_id(id)
|
|
||||||
if my_module.present?
|
|
||||||
my_module.name = new_name
|
|
||||||
my_module.save!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update connections for all modules in this project.
|
|
||||||
# Input is an array of arrays, where first element represents
|
|
||||||
# source node, and second element represents target node.
|
|
||||||
# Example input: [ [1, 2], [2, 3], [4, 5], [2, 5] ]
|
|
||||||
def update_module_connections(connections)
|
|
||||||
require 'rgl/base'
|
|
||||||
require 'rgl/adjacency'
|
|
||||||
require 'rgl/topsort'
|
|
||||||
|
|
||||||
dg = RGL::DirectedAdjacencyGraph.new
|
|
||||||
connections.each do |a,b|
|
|
||||||
# Check if both vertices exist
|
|
||||||
if (my_modules.find_all {|m| [a.to_i, b.to_i].include? m.id }).count == 2
|
|
||||||
dg.add_edge(a, b)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if cycles exist!
|
|
||||||
topsort = dg.topsort_iterator.to_a
|
|
||||||
if topsort.length == 0 and dg.edges.size > 1
|
|
||||||
raise ArgumentError, "Cycles exist."
|
|
||||||
end
|
|
||||||
|
|
||||||
# First, delete existing connections
|
|
||||||
# but keep a copy of previous state
|
|
||||||
previous_sources = {}
|
|
||||||
previous_sources.default = []
|
|
||||||
my_modules.each do |m|
|
|
||||||
previous_sources[m.id] = []
|
|
||||||
m.inputs.each do |c|
|
|
||||||
previous_sources[m.id] << c.from
|
|
||||||
end
|
|
||||||
end
|
|
||||||
my_modules.each do |m|
|
|
||||||
unless m.outputs.destroy_all
|
|
||||||
raise ActiveRecord::ActiveRecordError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Add new connections
|
|
||||||
filtered_edges = dg.edges.collect { |e| [e.source, e.target] }
|
|
||||||
filtered_edges.each do |a, b|
|
|
||||||
Connection.create!(:input_id => b, :output_id => a)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Unassign samples from former downstream modules
|
|
||||||
# for all destroyed connections
|
|
||||||
unassign_samples_from_old_downstream_modules(previous_sources)
|
|
||||||
|
|
||||||
visited = []
|
|
||||||
# Assign samples to all new downstream modules
|
|
||||||
filtered_edges.each do |a, b|
|
|
||||||
source = my_modules.find(a.to_i)
|
|
||||||
target = my_modules.find(b.to_i)
|
|
||||||
# Do this only for new edges
|
|
||||||
if previous_sources[target.id].exclude?(source)
|
|
||||||
# Go as high upstream as new edges take us
|
|
||||||
# and then assign samples to all downsteam samples
|
|
||||||
assign_samples_to_new_downstream_modules(previous_sources, visited, source)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Save topological order of modules (for modules without workflow,
|
|
||||||
# leave them unordered)
|
|
||||||
my_modules.each do |m|
|
|
||||||
if topsort.include? m.id.to_s
|
|
||||||
m.workflow_order = topsort.find_index(m.id.to_s)
|
|
||||||
else
|
|
||||||
m.workflow_order = -1
|
|
||||||
end
|
|
||||||
m.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Make sure to reload my modules, which now have updated connections and samples
|
|
||||||
my_modules.reload
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
# When connections are deleted, unassign samples that
|
|
||||||
# are not inherited anymore
|
|
||||||
def unassign_samples_from_old_downstream_modules(sources)
|
|
||||||
my_modules.each do |my_module|
|
|
||||||
sources[my_module.id].each do |s|
|
|
||||||
# Only do this for newly deleted connections
|
|
||||||
if s.outputs.map{|i| i.to}.exclude? my_module
|
|
||||||
my_module.get_downstream_modules.each do |dm|
|
|
||||||
# Get unique samples for all upstream modules
|
|
||||||
um = dm.get_upstream_modules
|
|
||||||
um.shift # remove current module
|
|
||||||
ums = um.map{|m| m.samples}.flatten.uniq
|
|
||||||
s.samples.each do |sample|
|
|
||||||
dm.samples.delete(sample) if ums.exclude? sample
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Assign samples to new connections recursively
|
|
||||||
def assign_samples_to_new_downstream_modules(sources, visited, my_module)
|
|
||||||
# If samples are already assigned for this module, stop going upstream
|
|
||||||
if visited.include? (my_module)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
visited << my_module
|
|
||||||
# Edge case, when module is source or it doesn't have any new input connections
|
|
||||||
if my_module.inputs.blank? or (
|
|
||||||
my_module.inputs.map{|c| c.from} -
|
|
||||||
sources[my_module.id]
|
|
||||||
).empty?
|
|
||||||
my_module.get_downstream_modules.each do |dm|
|
|
||||||
new_samples = my_module.samples.select { |el| dm.samples.exclude?(el) }
|
|
||||||
dm.samples.push(*new_samples)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
my_module.inputs.each do |input|
|
|
||||||
# Go upstream for new in connections
|
|
||||||
if sources[my_module.id].exclude?(input.from)
|
|
||||||
assign_samples_to_new_downstream_modules(input.from)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Updates positions of modules.
|
|
||||||
# Input is a map where keys are module IDs, and values are
|
|
||||||
# hashes like { x: <x>, y: <y> }.
|
|
||||||
def update_module_positions(positions)
|
|
||||||
positions.each do |id, pos|
|
|
||||||
unless MyModule.update(id, x: pos[:x], y: pos[:y])
|
|
||||||
raise ActiveRecord::ActiveRecordError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
my_modules.reload
|
|
||||||
end
|
|
||||||
|
|
||||||
# Normalize module positions in this project.
|
|
||||||
def normalize_module_positions
|
|
||||||
# This method normalizes module positions so x-s and y-s
|
|
||||||
# are all positive
|
|
||||||
x_diff = (my_modules.collect { |m| m.x }).min
|
|
||||||
y_diff = (my_modules.collect { |m| m.y }).min
|
|
||||||
|
|
||||||
my_modules.each do |m|
|
|
||||||
unless
|
|
||||||
m.update_attribute(:x, m.x - x_diff) and
|
|
||||||
m.update_attribute(:y, m.y - y_diff)
|
|
||||||
raise ActiveRecord::ActiveRecordError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Recalculate module groups in this project. Input is
|
|
||||||
# a hash of module ids and their corresponding module names.
|
|
||||||
def update_module_groups(module_groups, current_user)
|
|
||||||
require 'rgl/base'
|
|
||||||
require 'rgl/adjacency'
|
|
||||||
require 'rgl/connected_components'
|
|
||||||
|
|
||||||
dg = RGL::DirectedAdjacencyGraph[]
|
|
||||||
group_ids = Set.new
|
|
||||||
my_modules.where(archived: :false).each do |m|
|
|
||||||
unless m.my_module_group.blank?
|
|
||||||
group_ids << m.my_module_group.id
|
|
||||||
end
|
|
||||||
unless dg.has_vertex? m.id
|
|
||||||
dg.add_vertex m.id
|
|
||||||
end
|
|
||||||
m.outputs.each do |o|
|
|
||||||
dg.add_edge m.id, o.to.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
workflows = []
|
|
||||||
dg.to_undirected.each_connected_component { |w| workflows << w }
|
|
||||||
|
|
||||||
# Retrieve maximum allowed module group name
|
|
||||||
max_length = (MyModuleGroup.validators_on(:name).select { |v| v.class == ActiveModel::Validations::LengthValidator }).first.options[:maximum]
|
|
||||||
# For each workflow, generate new names
|
|
||||||
new_index = 1
|
|
||||||
wf_names = []
|
|
||||||
suffix = I18n.t("my_module_groups.new.suffix")
|
|
||||||
cut_index = -(suffix.length + 1)
|
|
||||||
workflows.each do |w|
|
|
||||||
modules = MyModule.find(w)
|
|
||||||
|
|
||||||
# Get an array of module names
|
|
||||||
names = []
|
|
||||||
modules.each do |m|
|
|
||||||
names << module_groups.fetch(m.id.to_s, "")
|
|
||||||
end
|
|
||||||
names = names.uniq
|
|
||||||
name = (names.select { |v| v != "" }).join(", ")
|
|
||||||
|
|
||||||
if w.length <= 1
|
|
||||||
name = nil
|
|
||||||
elsif name.blank?
|
|
||||||
name = I18n.t("my_module_groups.new.name", index: new_index)
|
|
||||||
new_index += 1
|
|
||||||
while MyModuleGroup.find_by(name: name).present?
|
|
||||||
name = I18n.t("my_module_groups.new.name", index: new_index)
|
|
||||||
new_index += 1
|
|
||||||
end
|
|
||||||
elsif name.length > max_length
|
|
||||||
# If length is too long, shorten it
|
|
||||||
name = name[0..(max_length + cut_index)] + suffix
|
|
||||||
end
|
|
||||||
|
|
||||||
wf_names << name
|
|
||||||
end
|
|
||||||
|
|
||||||
# Remove any existing module groups from modules
|
|
||||||
unless MyModuleGroup.destroy_all(:id => group_ids.to_a)
|
|
||||||
raise ActiveRecord::ActiveRecordError
|
|
||||||
end
|
|
||||||
|
|
||||||
# Second, create new groups
|
|
||||||
workflows.each_with_index do |w, i|
|
|
||||||
# Single modules are not considered part of any workflow
|
|
||||||
if w.length > 1
|
|
||||||
group = MyModuleGroup.new(
|
|
||||||
name: wf_names[i],
|
|
||||||
project: self,
|
|
||||||
my_modules: MyModule.find(w))
|
|
||||||
group.created_by = current_user
|
|
||||||
group.save!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
my_module_groups.reload
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -148,7 +148,7 @@ class Protocol < ActiveRecord::Base
|
||||||
|
|
||||||
def self.new_blank_for_module(my_module)
|
def self.new_blank_for_module(my_module)
|
||||||
Protocol.new(
|
Protocol.new(
|
||||||
organization: my_module.project.organization,
|
organization: my_module.experiment.project.organization,
|
||||||
protocol_type: :unlinked,
|
protocol_type: :unlinked,
|
||||||
my_module: my_module
|
my_module: my_module
|
||||||
)
|
)
|
||||||
|
@ -611,4 +611,4 @@ class Protocol < ActiveRecord::Base
|
||||||
self.parent.decrement!(:nr_of_linked_children) if self.parent.present?
|
self.parent.decrement!(:nr_of_linked_children) if self.parent.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -124,7 +124,7 @@ class Step < ActiveRecord::Base
|
||||||
if (protocol.my_module.present?) then
|
if (protocol.my_module.present?) then
|
||||||
Activity.create(
|
Activity.create(
|
||||||
type_of: :destroy_step,
|
type_of: :destroy_step,
|
||||||
project: protocol.my_module.project,
|
project: protocol.my_module.experiment.project,
|
||||||
my_module: protocol.my_module,
|
my_module: protocol.my_module,
|
||||||
user: @current_user,
|
user: @current_user,
|
||||||
message: I18n.t(
|
message: I18n.t(
|
||||||
|
|
|
@ -17,7 +17,7 @@ class UserProject < ActiveRecord::Base
|
||||||
|
|
||||||
def destroy_associations
|
def destroy_associations
|
||||||
# Destroy the user from all project's modules
|
# Destroy the user from all project's modules
|
||||||
project.my_modules.each do |my_module|
|
project.project_my_modules.each do |my_module|
|
||||||
um2 = (my_module.user_my_modules.select { |um| um.user == self.user }).first
|
um2 = (my_module.user_my_modules.select { |um| um.user == self.user }).first
|
||||||
if um2.present?
|
if um2.present?
|
||||||
um2.destroy
|
um2.destroy
|
||||||
|
|
Loading…
Add table
Reference in a new issue