Skip to content

Commit

Permalink
Public activity: Projects (#710)
Browse files Browse the repository at this point in the history
* Begin setting up public activity

* Added custom activity tracking to existing functionality, added basic translation views

* Moved generating of sample activity to underneath a project namespace

* Removed generate_activity calls and updated models to use tracked (which will create activities for crud operations. Created a concern for activity

* Removed views not required

* Changed colors for component, added current model to keep track of the current user so it can be used in models.

* Removed include not required

* Updated track activity concern to format activities to just include description and created date which will be used by the activity component to display the activity

* Moved project activity into scrollable div, refactored code

* Updated to include uploading/removal of sequencing data from a sample

* Updated to get the trackable if it was soft-deleted

* Refactored code, and updated to get activity trackable which are soft-deleted for members and namespace group links

* Removed rubocop disables

* Added custom activity for transferred samples

* Created new migration for public activity and set the belongs_to type to uuid for fields, updated to create custom attachment activities in services attached to the attachable instead of in the attachment model

* Fixed custom activity for transferring samples, formatted activity date/time

* Fixed gemfile lock file, updated setting of owner for activities to Current.user

* Reverted changes to gemfile.lock, updated attachments create and destroy service to create activites instead of the controller, updated projects transfer service to create activity

* chore: update Gemfile.lock after rebasing

* chore: fix issues during rebase

* Moved create activity from samples transfer controller to transfer service and updated to also create activity in project to which the samples were transferred. Added activities for cloning samples to source and target project

* Removed generate_activity code block that is no longer required

* Broke up activities into individual components, and translations are now done within the component rather than server side to allow us to use links

* Begin setting up public activity

* Added custom activity tracking to existing functionality, added basic translation views

* Moved generating of sample activity to underneath a project namespace

* Removed generate_activity calls and updated models to use tracked (which will create activities for crud operations. Created a concern for activity

* Removed views not required

* Changed colors for component, added current model to keep track of the current user so it can be used in models.

* Removed include not required

* Updated track activity concern to format activities to just include description and created date which will be used by the activity component to display the activity

* Moved project activity into scrollable div, refactored code

* Updated to include uploading/removal of sequencing data from a sample

* Removed rubocop disables

* Added custom activity for transferred samples

* Created new migration for public activity and set the belongs_to type to uuid for fields, updated to create custom attachment activities in services attached to the attachable instead of in the attachment model

* Fixed custom activity for transferring samples, formatted activity date/time

* Fixed gemfile lock file, updated setting of owner for activities to Current.user

* Reverted changes to gemfile.lock, updated attachments create and destroy service to create activites instead of the controller, updated projects transfer service to create activity

* chore: update Gemfile.lock after rebasing

* chore: fix issues during rebase

* Moved create activity from samples transfer controller to transfer service and updated to also create activity in project to which the samples were transferred. Added activities for cloning samples to source and target project

* Removed generate_activity code block that is no longer required

* Removed duplicate entry for public_activity gem

* Updated activity components class comments

* Updated translations and removed duplicate link to activity page left over from rebase

* Updated gemfile lockfile

* Updated project_activity_component to render links for sample clone and transfer, fixed current user not being set as owner for tracked, refactored code

* Refactored code to remove rubocop disables, updated sample create/update/destroy and sample attachment create/destroy services to create custom activities as using the out of box solution would cause the existing activity of a sample to get added to a project the sample was transferred to

* Removed code not required for this pr

* Fixed rubocop warnings

* Removed duplicate include of haspuid

* Updated text styling in _html translations for activities

* Fixed styling

* Added activity component preview

* Added custom activity for project sample metadata update

* Added activity component testing, removed components not required for this pr, fixed accessors for parameters set for activities

* Fixed Gemfile.lock file which had a bad rebase

* Refactored code to remove rubocop disable, activity parameters keys are now transformed before being added to the array of activities so we have consistent access to hash key/values, added missing key to namespace group link activity translation, updated translation to include html to bold name of group

* Refactored code to remove some more rubocop disables, fixed formatting

* Fixed rubocop warning, fixed formatting

* Removed scrollable container and updated to use full page scroll like the project samples page

* Moved activity-app to its own css rule to fix issue with scrollbar appearing when it was not necessary

* Updated to load 10 activities at a time using pagy, updated text for uploading/removing data from a sample

* Updated to use custom activities and removed automated model crud tracking, updated workflow execution completion service to add activities for automation bot attaching outputs and metadata to a sample from a workflow run

* Updated activity to display sample puid instead of it's name, added activity for creating an automated workflow execution

* Updated activities fixtures

* Added french translation placeholders

* Fixed translations

* Activity now displays project puid for transfer/clone instead of name

* Added missing translation in turbo stream file

* Fixed translation to show href for member when attributes updated for member, fixed rubocop warnings, fixed bug with activity displaying expanded if previously visited and load more button was clicked

* Normalized and fixed translation files

* Removed else statement which wasn't required for workflow activity component

* chore: Moved `Activities` and `History` project menu items

* Changed create_activity variable names, updated ui view components for activity, updated translations

* Updated destroy multiple samples service, and samples transfer and clone services calls to return sample puids that were deleted,clone,or transferred

---------

Co-authored-by: Eric Enns <[email protected]>
Co-authored-by: Josh Adam <[email protected]>
  • Loading branch information
3 people authored Sep 12, 2024
1 parent c41dfe2 commit 8c80ac6
Show file tree
Hide file tree
Showing 58 changed files with 1,174 additions and 41 deletions.
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ gem 'good_job', '~> 4.1', '>= 4.1.1'
gem 'roo', '~> 2.10.0'
gem 'roo-xls'

# activity tracking
gem 'public_activity'

# create zip file for data exports
gem 'zip_kit'

Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ GEM
prettier_print (1.2.1)
psych (5.1.2)
stringio
public_activity (3.0.1)
actionpack (>= 6.1.0)
activerecord (>= 6.1)
i18n (>= 0.5.0)
railties (>= 6.1.0)
public_suffix (6.0.1)
puma (6.4.2)
nio4r (~> 2.0)
Expand Down Expand Up @@ -693,6 +698,7 @@ DEPENDENCIES
pagy (~> 9.0.5)
paranoia
pg
public_activity
puma (~> 6.4)
rails (~> 7.2.0)
ransack (~> 4.2.1)
Expand Down
24 changes: 24 additions & 0 deletions app/components/activities/list_item_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<li class="mb-10 ms-4">
<div
class="
absolute w-3 h-3 bg-slate-200 rounded-full mt-1.5 -start-1.5 border border-white
dark:border-slate-700 dark:bg-slate-700
"
></div>
<div
class="
mb-1 text-sm font-normal leading-none text-slate-500 dark:text-slate-400
"
><%= helpers.local_time(activity[:created_at], :long) %></div>
<p class="mb-4 text-base font-normal text-slate-500 dark:text-slate-400">
<% if activity[:type] == 'Namespace' && activity[:key].include?('project_namespace') %>
<%= render Activities::ProjectActivityComponent.new(activity: activity) %>
<% elsif activity[:type] == 'WorkflowExecution' %>
<%= render Activities::WorkflowExecutionActivityComponent.new(activity: activity) %>
<% elsif activity[:type] == 'Member' %>
<%= render Activities::MemberActivityComponent.new(activity: activity) %>
<% elsif activity[:type] == 'NamespaceGroupLink' %>
<%= render Activities::NamespaceGroupLinkActivityComponent.new(activity: activity) %>
<% end %>
</p>
</li>
14 changes: 14 additions & 0 deletions app/components/activities/list_item_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module Activities
# Component for rendering an activity list item
class ListItemComponent < Component
attr_accessor :activity

def initialize(activity: nil, **system_arguments)
@activity = activity

@system_arguments = system_arguments
end
end
end
16 changes: 16 additions & 0 deletions app/components/activities/member_activity_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<%= t(
@activity[:key],
user: @activity[:user],
href:
link_to(
@activity[:member].user.email,
namespace_project_members_path(
@activity[:member].namespace.project.parent,
@activity[:member].namespace.project,
),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
namespace_type: @activity[:namespace_type],
name: @activity[:name],
member: @activity[:member].user.email,
) %>
12 changes: 12 additions & 0 deletions app/components/activities/member_activity_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

module Activities
# Component for rendering an activity of type Member
class MemberActivityComponent < Component
def initialize(activity: nil, **system_arguments)
@activity = activity

@system_arguments = system_arguments
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<%= t(
"#{@activity[:key]}",
user: @activity[:user],
href:
link_to(
@activity[:group].name,
group_path(@activity[:group]),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
namespace_type: @activity[:namespace_type],
name: @activity[:name],
group_name: @activity[:group].name,
) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

module Activities
# Component for rendering an activity of type NamespaceGroupLink
class NamespaceGroupLinkActivityComponent < Component
def initialize(activity: nil, **system_arguments)
@activity = activity

@system_arguments = system_arguments
end
end
end
53 changes: 53 additions & 0 deletions app/components/activities/project_activity_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<% if samples_link %>
<% project =
(
if @activity[:source_project].present?
@activity[:source_project]
else
@activity[:target_project]
end
) %>
<%= t(
"#{@activity[:key]}",
user: @activity[:user],
href:
link_to(
project.puid,
namespace_project_samples_path(project.parent, project),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
transferred_samples_count: @activity[:transferred_samples_ids]&.size,
transferred_samples_ids: @activity[:transferred_samples_ids],
transferred_samples_puids: @activity[:transferred_samples_puids],
cloned_samples_count: @activity[:cloned_samples_ids]&.size,
cloned_samples_ids: @activity[:cloned_samples_ids],
cloned_samples_puids: @activity[:cloned_samples_puids],
) %>
<% elsif sample_link %>
<% url =
namespace_project_sample_path(
@activity[:current_project].parent,
@activity[:current_project].project,
id: @activity[:sample_id],
) %>
<%= t(
"#{@activity[:key]}",
user: @activity[:user],
href:
link_to(
@activity[:sample_puid],
path_with_params(url, { tab: samples_tab }),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
) %>
<% else %>
<%= t(
"#{@activity[:key]}",
user: @activity[:user],
name: @activity[:name],
old_namespace: @activity[:old_namespace],
new_namespace: @activity[:new_namespace],
sample_puid: @activity[:sample_puid],
deleted_count: @activity[:deleted_count],
) %>
<% end %>
28 changes: 28 additions & 0 deletions app/components/activities/project_activity_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module Activities
# Component for rendering an activity of type Namespace for Projects
class ProjectActivityComponent < Component
include PathHelper

def initialize(activity: nil, **system_arguments)
@activity = activity

@system_arguments = system_arguments
end

def sample_link
@activity[:action] == 'sample_create' || @activity[:action] == 'sample_update' ||
@activity[:action] == 'attachment_create' || @activity[:action] == 'attachment_destroy' ||
@activity[:action] == 'metadata_update'
end

def samples_link
@activity[:action] == 'sample_clone' || @activity[:action] == 'sample_transfer'
end

def samples_tab
@activity[:action] == 'metadata_update' ? 'metadata' : ''
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<% if @activity[:automated] == true %>
<%= t(
"#{@activity[:key]}",
user: @activity[:user],
href:
link_to(
@activity[:workflow_id],
namespace_project_automated_workflow_executions_path(
@activity[:namespace].parent,
@activity[:namespace].project,
),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
) %>
<% else %>
<%= t(
"#{@activity[:key]}",
user: @activity[:user],
href:
link_to(
@activity[:workflow_id],
namespace_project_workflow_execution_path(
@activity[:namespace].parent,
@activity[:namespace].project,
@activity[:workflow_id],
),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
sample_href:
link_to(
@activity[:sample_puid],
namespace_project_sample_path(
@activity[:namespace].parent,
@activity[:namespace].project,
@activity[:sample_id],
),
class: "text-slate-800 dark:text-slate-300 font-medium hover:underline",
),
) %>
<% end %>
12 changes: 12 additions & 0 deletions app/components/activities/workflow_execution_activity_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

module Activities
# Component for rendering an activity of type WorkflowExecution
class WorkflowExecutionActivityComponent < Component
def initialize(activity: nil, **system_arguments)
@activity = activity

@system_arguments = system_arguments
end
end
end
17 changes: 17 additions & 0 deletions app/components/activity_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div>
<ol class="relative border-s border-slate-200 dark:border-slate-700">
<%= helpers.turbo_frame_tag "activities" do %>
<% activities.each do |activity| %>
<%= render Activities::ListItemComponent.new(activity: activity) %>
<% end %>
<% end %>
</ol>

<div id="next_link">
<% if @pagy.next.present? %>
<%= button_to t(:"components.activity.load_more"),
helpers.pagy_url_for(@pagy, @pagy.next),
class: "button button--state-default button--size-default" %>
<% end %>
</div>
</div>
12 changes: 12 additions & 0 deletions app/components/activity_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

# Component to render activity
class ActivityComponent < Component
attr_accessor :activities, :pagy

def initialize(activities:, pagy:, **system_arguments)
@activities = activities
@pagy = pagy
@system_arguments = system_arguments
end
end
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# Main application controller
class ApplicationController < ActionController::Base
include PublicActivity::StoreController
include Irida::Auth
include Pagy::Backend
include RouteHelper
Expand Down
14 changes: 13 additions & 1 deletion app/controllers/projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,17 @@ def update # rubocop:disable Metrics/MethodLength, Metrics/AbcSize

def activity
authorize! @project
# No necessary code here

project_activities = @project.namespace.retrieve_project_activity.order(created_at: :desc)

@pagy, raw_activities = pagy(project_activities, limit: 10)

@activities = @project.namespace.human_readable_activity(raw_activities)

respond_to do |format|
format.html
format.turbo_stream
end
end

def transfer
Expand Down Expand Up @@ -171,6 +181,8 @@ def current_page
t(:'general.default_sidebar.projects')
when 'history'
t(:'projects.sidebar.history')
when 'activity'
t(:'projects.sidebar.activity')
else
t(:'projects.sidebar.general')
end
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/path_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def path_with_params(url, params)
end

params.each do |param, value|
query[param] = value
query[param] = value if value.present?
end

parsed.query = URI.encode_www_form(query)
Expand Down
Loading

0 comments on commit 8c80ac6

Please sign in to comment.