Implement storage location activities [SCI-10925]

This commit is contained in:
Martin Artnik 2024-09-11 14:36:14 +02:00
parent 8d5adad6b5
commit c4c14553ff
10 changed files with 233 additions and 71 deletions

View file

@ -17,30 +17,36 @@ class StorageLocationRepositoryRowsController < ApplicationController
meta: (pagination_dict(storage_location_repository_row) unless @storage_location.with_grid?)
end
def update
@storage_location_repository_row.update(storage_location_repository_row_params)
def create
ActiveRecord::Base.transaction do
@storage_location_repository_row = StorageLocationRepositoryRow.new(
repository_row: @repository_row,
storage_location: @storage_location,
metadata: storage_location_repository_row_params[:metadata] || {},
created_by: current_user
)
if @storage_location_repository_row.save
render json: @storage_location_repository_row,
serializer: Lists::StorageLocationRepositoryRowSerializer
else
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
if @storage_location_repository_row.save
log_activity(:storage_location_repository_row_created)
render json: @storage_location_repository_row,
serializer: Lists::StorageLocationRepositoryRowSerializer
else
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
end
end
end
def create
@storage_location_repository_row = StorageLocationRepositoryRow.new(
repository_row: @repository_row,
storage_location: @storage_location,
metadata: storage_location_repository_row_params[:metadata] || {},
created_by: current_user
)
def update
ActiveRecord::Base.transaction do
@storage_location_repository_row.update(storage_location_repository_row_params)
if @storage_location_repository_row.save
render json: @storage_location_repository_row,
serializer: Lists::StorageLocationRepositoryRowSerializer
else
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
if @storage_location_repository_row.save
log_activity(:storage_location_repository_row_moved)
render json: @storage_location_repository_row,
serializer: Lists::StorageLocationRepositoryRowSerializer
else
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
end
end
end
@ -53,7 +59,7 @@ class StorageLocationRepositoryRowsController < ApplicationController
metadata: storage_location_repository_row_params[:metadata] || {},
created_by: current_user
)
log_activity(:storage_location_repository_row_moved)
render json: @storage_location_repository_row,
serializer: Lists::StorageLocationRepositoryRowSerializer
rescue ActiveRecord::RecordInvalid => e
@ -63,10 +69,13 @@ class StorageLocationRepositoryRowsController < ApplicationController
end
def destroy
if @storage_location_repository_row.discard
render json: {}
else
render json: { errors: @storage_location_repository_row.errors.full_messages }, status: :unprocessable_entity
ActiveRecord::Base.transaction do
if @storage_location_repository_row.discard
log_activity(:storage_location_repository_row_deleted)
render json: {}
else
render json: { errors: @storage_location_repository_row.errors.full_messages }, status: :unprocessable_entity
end
end
end
@ -74,7 +83,7 @@ class StorageLocationRepositoryRowsController < ApplicationController
render json: {
actions: Toolbars::StorageLocationRepositoryRowsService.new(
current_user,
items_ids: JSON.parse(params[:items]).map { |i| i['id'] }
items_ids: JSON.parse(params[:items]).pluck('id')
).actions
}
end
@ -116,4 +125,18 @@ class StorageLocationRepositoryRowsController < ApplicationController
def check_manage_permissions
render_403 unless can_manage_storage_location?(@storage_location)
end
def log_activity(type_of, message_items = {})
Activities::CreateActivityService
.call(activity_type: type_of,
owner: current_user,
team: @storage_location.team,
subject: @storage_location_repository_row.repository_row,
message_items: {
storage_location: @storage_location_repository_row.storage_location_id,
repository_row: @storage_location_repository_row.repository_row_id,
position: @storage_location_repository_row.human_readable_position,
user: current_user.id
}.merge(message_items))
end
end

View file

@ -21,60 +21,78 @@ class StorageLocationsController < ApplicationController
def show; end
def update
@storage_location.image.purge if params[:file_name].blank?
@storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
@storage_location.update(storage_location_params)
def create
ActiveRecord::Base.transaction do
@storage_location = StorageLocation.new(
storage_location_params.merge({ created_by: current_user })
)
if @storage_location.save
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
@storage_location.team = @storage_location.root_storage_location.team || current_team
@storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
if @storage_location.save
log_activity('storage_location_created')
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
end
end
end
def create
@storage_location = StorageLocation.new(
storage_location_params.merge({ created_by: current_user })
)
def update
ActiveRecord::Base.transaction do
@storage_location.image.purge if params[:file_name].blank?
@storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
@storage_location.update(storage_location_params)
@storage_location.team = @storage_location.root_storage_location.team || current_team
@storage_location.image.attach(params[:signed_blob_id]) if params[:signed_blob_id]
if @storage_location.save
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
if @storage_location.save
log_activity('storage_location_edited')
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
end
end
end
def destroy
if @storage_location.discard
render json: {}
else
render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
ActiveRecord::Base.transaction do
if @storage_location.discard
log_activity('storage_location_deleted')
render json: {}
else
render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
end
end
end
def duplicate
new_storage_location = @storage_location.duplicate!
if new_storage_location
render json: new_storage_location, serializer: Lists::StorageLocationSerializer
else
render json: { errors: :failed }, status: :unprocessable_entity
ActiveRecord::Base.transaction do
new_storage_location = @storage_location.duplicate!
if new_storage_location
@storage_location = new_storage_location
log_activity('storage_location_created')
render json: @storage_location, serializer: Lists::StorageLocationSerializer
else
render json: { errors: :failed }, status: :unprocessable_entity
end
end
end
def move
storage_location_destination =
if move_params[:destination_storage_location_id] == 'root_storage_location'
nil
else
current_team.storage_locations.find(move_params[:destination_storage_location_id])
end
ActiveRecord::Base.transaction do
original_storage_location = @storage_location.parent
destination_storage_location =
if move_params[:destination_storage_location_id] == 'root_storage_location'
nil
else
current_team.storage_locations.find(move_params[:destination_storage_location_id])
end
@storage_location.update!(parent: storage_location_destination)
@storage_location.update!(parent: destination_storage_location)
log_activity('storage_location_moved', { storage_location_original: original_storage_location.id, storage_location_destination: destination_storage_location.id })
end
render json: { message: I18n.t('storage_locations.index.move_modal.success_flash') }
rescue StandardError => e
@ -93,7 +111,11 @@ class StorageLocationsController < ApplicationController
end
def unassign_rows
@storage_location.storage_location_repository_rows.where(id: params[:ids]).discard_all
ActiveRecord::Base.transaction do
@storage_location_repository_rows = @storage_location.storage_location_repository_rows.where(id: params[:ids])
@storage_location_repository_rows.each(&:discard)
log_unassign_activities
end
render json: { status: :ok }
end
@ -206,4 +228,32 @@ class StorageLocationsController < ApplicationController
}
end
end
def log_activity(type_of, message_items = {})
Activities::CreateActivityService
.call(activity_type: "#{'container_' if @storage_location.container}#{type_of}",
owner: current_user,
team: @storage_location.team,
subject: @storage_location,
message_items: {
storage_location: @storage_location.id,
user: current_user.id
}.merge(message_items))
end
def log_unassign_activities
@storage_location_repository_rows.each do |storage_location_repository_row|
Activities::CreateActivityService
.call(activity_type: :storage_location_repository_row_deleted,
owner: current_user,
team: @storage_location.team,
subject: storage_location_repository_row.repository_row,
message_items: {
storage_location: storage_location_repository_row.storage_location_id,
repository_row: storage_location_repository_row.repository_row_id,
position: storage_location_repository_row.human_readable_position,
user: current_user.id
})
end
end
end

View file

@ -71,7 +71,7 @@ class TeamSharedObjectsController < ApplicationController
}
end
def log_activity(type_of, team_shared_object)
def log_activity(type_of)
# log activity logic
end
end

View file

@ -108,6 +108,8 @@ module GlobalActivitiesHelper
else
project_folder_path(obj, team: obj.team.id)
end
when StorageLocation
path = storage_location_path(obj)
else
return current_value
end

View file

@ -187,6 +187,9 @@ class Activity < ApplicationRecord
when Asset
breadcrumbs[:asset] = subject.blob.filename.to_s
generate_breadcrumb(subject.result || subject.step || subject.repository_cell.repository_row.repository)
when StorageLocation
breadcrumbs[:storage_location] = subject.name
generate_breadcrumb(subject.team)
end
end

View file

@ -14,10 +14,17 @@ class StorageLocationRepositoryRow < ApplicationRecord
validate :ensure_uniq_position
end
def human_readable_position
return unless metadata['position']
column_letter = ('A'..'Z').to_a[metadata['position'][0] - 1]
row_number = metadata['position'][1]
"#{column_letter}#{row_number}"
end
def position_must_be_present
if metadata['position'].blank?
errors.add(:base, I18n.t('activerecord.errors.models.storage_location.missing_position'))
end
errors.add(:base, I18n.t('activerecord.errors.models.storage_location.missing_position')) if metadata['position'].blank?
end
def ensure_uniq_position

View file

@ -61,7 +61,11 @@ module Activities
end
if id
obj = const.find id
obj = if const.respond_to?(:with_discarded)
const.with_discarded.find id
else
const.find id
end
@activity.message_items[k] = { type: const.to_s, value: obj.public_send(getter_method).to_s, id: id }
@activity.message_items[k][:value_for] = getter_method
@activity.message_items[k][:value_type] = value_type unless value_type.nil?

View file

@ -0,0 +1,15 @@
<%= render partial: "global_activities/references/team",
locals: { team: team, subject: team, breadcrumbs: breadcrumbs, values: values, type_of: type_of } %>
<div class="ga-breadcrumb">
<span class="sn-icon sn-icon-storage"></span>
<% if subject %>
<%= route_to_other_team(storage_location_path(subject.id, team: subject.team.id),
team,
subject.name&.truncate(Constants::NAME_TRUNCATION_LENGTH),
title: subject.name) %>
<% else %>
<span title="<%= breadcrumbs['storage_location'] %>">
<%= breadcrumbs['storage_location']&.truncate(Constants::NAME_TRUNCATION_LENGTH) %>
</span>
<% end %>
</div>

View file

@ -188,7 +188,7 @@ class Extends
ACTIVITY_SUBJECT_TYPES = %w(
Team RepositoryBase Project Experiment MyModule Result Protocol Report RepositoryRow
ProjectFolder Asset Step LabelTemplate
ProjectFolder Asset Step LabelTemplate StorageLocation StorageLocationRepositoryRow
).freeze
SEARCHABLE_ACTIVITY_SUBJECT_TYPES = %w(
@ -205,7 +205,8 @@ class Extends
my_module: %i(results protocols),
result: [:assets],
protocol: [:steps],
step: [:assets]
step: [:assets],
storage_location: [:storage_location_repository_rows]
}
ACTIVITY_MESSAGE_ITEMS_TYPES =
@ -495,7 +496,24 @@ class Extends
task_step_asset_renamed: 305,
result_asset_renamed: 306,
protocol_step_asset_renamed: 307,
inventory_items_added_or_updated_with_import: 308
inventory_items_added_or_updated_with_import: 308,
storage_location_created: 309,
storage_location_deleted: 310,
storage_location_edited: 311,
storage_location_moved: 312,
storage_location_shared: 313,
storage_location_unshared: 314,
storage_location_sharing_updated: 315,
container_storage_location_created: 316,
container_storage_location_deleted: 317,
container_storage_location_edited: 318,
container_storage_location_moved: 319,
container_storage_location_shared: 320,
container_storage_location_unshared: 321,
container_storage_location_sharing_updated: 322,
storage_location_repository_row_created: 323,
storage_location_repository_row_deleted: 324,
storage_location_repository_row_moved: 325
}
ACTIVITY_GROUPS = {
@ -515,7 +533,10 @@ class Extends
190, 191, *204..215, 220, 223, 227, 228, 229, *230..235,
*237..240, *253..256, *279..283, 300, 304, 307],
team: [92, 94, 93, 97, 104, 244, 245],
label_templates: [*216..219]
label_templates: [*216..219],
storage_locations: [*309..315],
container_storage_location: [*316..322],
storage_location_repository_rows: [*323..325]
}
TOP_LEVEL_ASSIGNABLES = %w(Project Team Protocol Repository).freeze

View file

@ -322,6 +322,23 @@ en:
protocol_step_asset_renamed_html: "%{user} renamed file %{old_name} to %{new_name} on protocols step <strong>%{step}</strong> in Protocol repository."
result_asset_renamed_html: "%{user} renamed file %{old_name} to %{new_name} on result <strong>%{result}</strong> on task <strong>%{my_module}</strong>."
item_added_with_import_html: "%{user} edited %{num_of_items} inventory item(s) in %{repository}."
storage_location_created_html: "%{user} created location %{storage_location}."
storage_location_deleted_html: "%{user} deleted location %{storage_location}."
storage_location_edited_html: "%{user} edited location %{storage_location}."
storage_location_moved_html: "%{user} moved location %{storage_location} from %{storage_location_original} to %{storage_location_destination}."
storage_location_shared_html: "%{user} shared location %{storage_location} with team %{team} with %{permission_level} permission."
storage_location_unshared_html: "%{user} unshared location %{storage_location} with team %{team}."
storage_location_sharing_updated_html: "%{user} changed permission of shared location %{storage_location} with team %{team} to %{permission_level}."
container_storage_location_created_html: "%{user} created box %{storage_location}."
container_storage_location_deleted_html: "%{user} deleted box %{storage_location}."
container_storage_location_edited_html: "%{user} edited box %{storage_location}."
container_storage_location_moved_html: "%{user} moved box %{storage_location} from %{storage_location_original} to %{storage_location_destination}."
container_storage_location_shared_html: "%{user} shared box %{storage_location} with team %{team} with %{permission_level} permission."
container_storage_location_unshared_html: "%{user} unshared box %{storage_location} with team %{team}."
container_storage_location_sharing_updated_html: "%{user} changed permission of shared box %{storage_location} with team %{team} to %{permission_level}."
storage_location_repository_row_created_html: "%{user} assigned %{repository_row} to box %{storage_location} %{position}."
storage_location_repository_row_deleted_html: "%{user} unassigned %{repository_row} from box %{storage_location} %{position}."
storage_location_repository_row_moved_html: "%{user} moved item %{repository_row} from box %{storage_location_original} %{positions} to box %{storage_location_destination} %{positions}."
activity_name:
create_project: "Project created"
rename_project: "Project renamed"
@ -601,6 +618,23 @@ en:
task_step_file_duplicated: "File attachment on Task step duplicated"
result_file_duplicated: "File attachment on Task result duplicated"
protocol_step_file_duplicated: "File attachment on Protocol step duplicated"
storage_location_created: "Location created"
storage_location_deleted: "Location deleted"
storage_location_edited: "Location edited"
storage_location_moved: "Location moved"
storage_location_shared: "Location shared"
storage_location_unshared: "Location unshared"
storage_location_sharing_updated: "Location sharing permission updated"
container_storage_location_created: "Box created"
container_storage_location_deleted: "Box deleted"
container_storage_location_edited: "Box edited"
container_storage_location_moved: "Box moved"
container_storage_location_shared: "Box shared"
container_storage_location_unshared: "Box unshared"
container_storage_location_sharing_updated: "Box sharing permission updated"
storage_location_repository_row_created: "Inventory item location assigned"
storage_location_repository_row_deleted: "Inventory item location unassigned"
storage_location_repository_row_moved: "Inventory item location moved"
activity_group:
projects: "Projects"
task_results: "Task results"
@ -614,6 +648,8 @@ en:
team: "Team"
exports: "Exports"
label_templates: "Label templates"
storage_locations: "Locations"
container_storage_locations: "Boxes"
subject_name:
repository: "Inventory"
project: "Project"
@ -623,3 +659,4 @@ en:
protocol: "Protocol"
step: "Step"
report: "Report"
storage_location: "Location"