From 2101c2d694f44c6adaaac5123258e6fef3b6ffe6 Mon Sep 17 00:00:00 2001 From: Adam Coffman Date: Thu, 18 Apr 2024 16:32:12 -0500 Subject: [PATCH 1/6] tweak admin to get working with features --- server/app/admin/assertions_admin.rb | 4 +-- server/app/admin/evidence_items_admin.rb | 6 ++-- .../{genes_admin.rb => features_admin.rb} | 36 +++++++++---------- server/app/admin/variants_admin.rb | 13 ++----- server/config/environments/development.rb | 2 +- server/config/initializers/trestle.rb | 1 - 6 files changed, 26 insertions(+), 36 deletions(-) rename server/app/admin/{genes_admin.rb => features_admin.rb} (71%) diff --git a/server/app/admin/assertions_admin.rb b/server/app/admin/assertions_admin.rb index ecb619639..0957b5567 100644 --- a/server/app/admin/assertions_admin.rb +++ b/server/app/admin/assertions_admin.rb @@ -1,10 +1,10 @@ Trestle.resource(:assertions) do collection do - Assertion.eager_load(:flags, molecular_profile: {variants: [:gene]}).order(id: :asc) + Assertion.eager_load(:flags, molecular_profile: {variants: [:feature]}).order(id: :asc) end search do |q| - q ? collection.where("genes.name ILIKE ?", "#{q}%").or(collection.where("variants.name ILIKE ?", "#{q}%")) : collection + q ? collection.where("features.name ILIKE ?", "#{q}%").or(collection.where("variants.name ILIKE ?", "#{q}%")) : collection end remove_action :destroy diff --git a/server/app/admin/evidence_items_admin.rb b/server/app/admin/evidence_items_admin.rb index 3c456003b..c518461f8 100644 --- a/server/app/admin/evidence_items_admin.rb +++ b/server/app/admin/evidence_items_admin.rb @@ -1,6 +1,6 @@ Trestle.resource(:evidence_items) do collection do - EvidenceItem.eager_load(:flags, molecular_profile: {variants: [:gene]}).order(id: :asc) + EvidenceItem.eager_load(:flags, molecular_profile: {variants: [:feature]}).order(id: :asc) end remove_action :destroy @@ -37,7 +37,7 @@ tab :evidence_item do row do col(sm: 1) { static_field :id } - col(sm: 1) { static_field evidence_item.molecular_profile.display_name } + col(sm: 1) { evidence_item.molecular_profile.display_name } col(sm: 2) do variant_origins = EvidenceItem.variant_origins.keys.map { |variant_origin| [variant_origin, variant_origin] } select :variant_origin, variant_origins @@ -95,7 +95,7 @@ select :phenotype_ids, Phenotype.order(:hpo_class), { label: "Phenotypes" }, multiple: true - collection_select :source_id, Source.order(:description), :id, :display_name + collection_select :source_id, Source.order(:citation), :id, :display_name divider diff --git a/server/app/admin/genes_admin.rb b/server/app/admin/features_admin.rb similarity index 71% rename from server/app/admin/genes_admin.rb rename to server/app/admin/features_admin.rb index 342f2dc2b..4f6c9f2f9 100644 --- a/server/app/admin/genes_admin.rb +++ b/server/app/admin/features_admin.rb @@ -1,41 +1,39 @@ -Trestle.resource(:genes, model: Features::Gene) do +Trestle.resource(:features) do collection do - Fatures::Gene.includes(:flags).order(name: :asc) + Fature.includes(:flags).order(name: :asc) end search do |q| - q ? collection.where("genes.name ILIKE ?", "#{q}%") : collection + q ? collection.where("features.name ILIKE ?", "#{q}%") : collection end remove_action :destroy remove_action :new menu do - item :genes, icon: "fa fa-align-center" #TODO: see if we can use our custom icons here + item :features, icon: "fa fa-align-center" #TODO: see if we can use our custom icons here end scope :all - scope :with_variants, -> { Features::Gene.joins(:variants).distinct }, default: true - scope :flagged, -> { Features::Gene.where(flagged: true) } + scope :with_variants, -> { Feature.joins(:variants).distinct }, default: true + scope :flagged, -> { Features.where(flagged: true) } # Customize the table columns shown on the index view. table do column :id - column :entrez_id column :name - column :official_name + column :full_name column :flagged end # Customize the form fields shown on the new/edit views. - form do |gene| - tab :gene do + form do |feature| + tab :feature do row do col(sm: 1) { static_field :id } - col(sm: 1) { static_field :entrez_id } col(sm: 1) { static_field :name } - col(sm: 8) { static_field :official_name } - if gene.flagged + col(sm: 8) { static_field :full_name } + if feature.flagged col do static_field :flagged do status_tag(icon("fa fa-flag"), :danger) @@ -54,8 +52,8 @@ end end - tab :comments, badge: gene.comments.size do - table gene.comments do + tab :comments, badge: feature.comments.size do + table feature.comments do column :id do |comment| link_to comment.id, CommentsAdmin.instance_path(comment) end @@ -67,8 +65,8 @@ end end - tab :flags, badge: gene.flags.where(state: 'open').exists? do - table gene.flags do + tab :flags, badge: feature.flags.where(state: 'open').exists? do + table feature.flags do column :id do |flag| link_to flag.id, FlagsAdmin.instance_path(flag) end @@ -82,8 +80,8 @@ end end - tab :revisions, badge: gene.revisions.where(status: 'new').exists? do - table gene.revisions do + tab :revisions, badge: feature.revisions.where(status: 'new').exists? do + table feature.revisions do column :id do |revision| link_to revision.id, RevisionsAdmin.instance_path(revision) end diff --git a/server/app/admin/variants_admin.rb b/server/app/admin/variants_admin.rb index d219ce4d9..41736867a 100644 --- a/server/app/admin/variants_admin.rb +++ b/server/app/admin/variants_admin.rb @@ -20,7 +20,7 @@ table do column :id column :name - column :gene + column :feature column :variant_aliases do |variant| variant.variant_aliases.map {|a| a.name }.join(', ') end @@ -37,15 +37,8 @@ col(sm: 1) { static_field :id } col(sm: 1) { static_field :name } col(sm: 1) do - static_field :gene do - link_to variant.gene.name, GenesAdmin.instance_path(variant.gene) - end - end - if variant.secondary_gene.present? - col(sm: 1) do - static_field :secondary_gene do - link_to variant.secondary_gene.name, GenesAdmin.instance_path(variant.secondary_gene) - end + static_field :variant do + link_to variant.feature.name, FeaturesAdmin.instance_path(variant.feature) end end if variant.flagged diff --git a/server/config/environments/development.rb b/server/config/environments/development.rb index c129b5ff8..4961004f1 100644 --- a/server/config/environments/development.rb +++ b/server/config/environments/development.rb @@ -69,5 +69,5 @@ # config.action_view.annotate_rendered_view_with_filenames = true # Raise error when a before_action's only/except options reference missing actions - config.action_controller.raise_on_missing_callback_actions = true + config.action_controller.raise_on_missing_callback_actions = false end diff --git a/server/config/initializers/trestle.rb b/server/config/initializers/trestle.rb index c2365ef4a..6b6276d24 100644 --- a/server/config/initializers/trestle.rb +++ b/server/config/initializers/trestle.rb @@ -79,7 +79,6 @@ unless current_user && Role.user_is_at_least_a?(current_user, :admin) redirect_to '/' end - ActiveStorage::Current.host = request.base_url end # # config.after_action do |controller| From 80c982c94f9b2ae7c0ce95045e430f1dbc1f8f11 Mon Sep 17 00:00:00 2001 From: Adam Coffman Date: Thu, 18 Apr 2024 16:33:02 -0500 Subject: [PATCH 2/6] rudimentary reporting framework for custom reports --- server/app/admin/reports_admin.rb | 58 +++++++++++++++ server/app/admin/utilities_admin.rb | 35 ++++++++++ server/app/models/actions/merge_accounts.rb | 44 ++++++------ server/app/reports/clingen_counts.rb | 58 +++++++++++++++ server/app/reports/report.rb | 70 +++++++++++++++++++ server/app/utilities/action_wrapper.rb | 28 ++++++++ server/app/utilities/merge_accounts.rb | 21 ++++++ server/app/views/admin/reports/index.html.erb | 17 +++++ .../app/views/admin/reports/result.html.erb | 32 +++++++++ server/app/views/admin/reports/show.html.erb | 30 ++++++++ server/app/views/admin/shared/_field.html.erb | 13 ++++ .../app/views/admin/utilities/index.html.erb | 17 +++++ .../app/views/admin/utilities/result.html.erb | 32 +++++++++ .../app/views/admin/utilities/show.html.erb | 19 +++++ 14 files changed, 451 insertions(+), 23 deletions(-) create mode 100644 server/app/admin/reports_admin.rb create mode 100644 server/app/admin/utilities_admin.rb create mode 100644 server/app/reports/clingen_counts.rb create mode 100644 server/app/reports/report.rb create mode 100644 server/app/utilities/action_wrapper.rb create mode 100644 server/app/utilities/merge_accounts.rb create mode 100644 server/app/views/admin/reports/index.html.erb create mode 100644 server/app/views/admin/reports/result.html.erb create mode 100644 server/app/views/admin/reports/show.html.erb create mode 100644 server/app/views/admin/shared/_field.html.erb create mode 100644 server/app/views/admin/utilities/index.html.erb create mode 100644 server/app/views/admin/utilities/result.html.erb create mode 100644 server/app/views/admin/utilities/show.html.erb diff --git a/server/app/admin/reports_admin.rb b/server/app/admin/reports_admin.rb new file mode 100644 index 000000000..1b3a9a2e1 --- /dev/null +++ b/server/app/admin/reports_admin.rb @@ -0,0 +1,58 @@ +Trestle.admin(:reports) do + menu do + item :reports, icon: "fas fa-file-alt" + end + + controller do + def index + @reports = Report::AVAILABLE_REPORTS + end + + def show + @report = Report::AVAILABLE_REPORTS.find { |x| params[:name] == x.name } + end + + def generate_report + @report = Report::AVAILABLE_REPORTS.find { |x| params[:name] == x.name } + report_params = params.permit(@report.inputs.keys).to_h + report_instance = @report.new(report_params.symbolize_keys) + report_instance.perform unless report_instance.errors.any? + + if report_instance.errors.any? + flash[:error] = report_instance.errors.join("\n") + render :show + else + if params[:format] == "download" + stream_table(report_instance) + else + @data = report_instance.data + @headers = report_instance.headers + render :result + end + end + end + + private + def stream_table(report) + require 'csv' + headers.delete("Content-Length") + headers["Cache-Control"] = "no-cache" + headers["Content-Type"] = "text/csv" + headers["Content-Disposition"] = "attachment; filename=\"#{report.class.name}-#{Date.today}.tsv\"" + headers["X-Accel-Buffering"] = "no" + response.status = 200 + + self.response_body = Enumerator.new do |stream| + stream << CSV.generate_line(report.headers, col_sep: "\t") + report.data.each do |row| + stream << CSV.generate_line(row, col_sep: "\t") + end + end + end + end + + routes do + get '/:name', action: :show + post '/:name', action: :generate_report + end +end diff --git a/server/app/admin/utilities_admin.rb b/server/app/admin/utilities_admin.rb new file mode 100644 index 000000000..c099b77a5 --- /dev/null +++ b/server/app/admin/utilities_admin.rb @@ -0,0 +1,35 @@ +Trestle.admin(:utilities) do + menu do + item :utilities, icon: "fas fa-cogs" + end + + controller do + def index + @utilities = ActionWrapper::AVAILABLE_ACTIONS + end + + def show + @util = ActionWrapper::AVAILABLE_ACTIONS.find { |x| params[:name] == x.name } + end + + def perform_action + @util = ActionWrapper::AVAILABLE_ACTIONS.find { |x| params[:name] == x.name } + util_params = params.permit(@util.inputs.keys).to_h + action = @util.new + res = action.perform(util_params.symbolize_keys) + + if res.errors.any? + flash[:error] = res.errors.join("\n") + else + flash[:message] = "#{@util.name} Succeeded" + end + + render :show + end + end + + routes do + get '/:name', action: :show + post '/:name', action: :perform_action + end +end diff --git a/server/app/models/actions/merge_accounts.rb b/server/app/models/actions/merge_accounts.rb index 90c8d52b2..752423ec6 100644 --- a/server/app/models/actions/merge_accounts.rb +++ b/server/app/models/actions/merge_accounts.rb @@ -2,12 +2,12 @@ module Actions class MergeAccounts include Actions::Transactional - attr_reader :account_to_keep, :accounts_to_merge, :old_account_ids + attr_reader :account_to_keep, :account_to_merge, :old_account_id - def initialize(account_to_keep:, accounts_to_merge_in:) - @account_to_keep = account_to_keep - @accounts_to_merge = Array(accounts_to_merge_in) - @old_account_ids = @accounts_to_merge.map(&:id) + def initialize(account_id_to_keep:, account_id_to_merge_in:) + @account_to_keep = User.find(account_id_to_keep) + @old_account_id = account_id_to_merge_in + @account_to_merge = User.find(account_id_to_merge_in) end def execute @@ -24,12 +24,12 @@ def execute merge_organizations move_legacy_changes account_to_keep.save! - accounts_to_merge.each { |u| u.destroy! } + account_to_merge.destroy! end private def move_authorizations - auths = Authorization.where(user_id: old_account_ids) + auths = Authorization.where(user_id: old_account_id) auths.each do |a| a.user_id = account_to_keep.id a.save! @@ -37,7 +37,7 @@ def move_authorizations end def move_activities - activities = Activity.where(user_id: old_account_ids) + activities = Activity.where(user_id: old_account_id) activities.each do |a| a.user_id = account_to_keep.id a.save! @@ -45,7 +45,7 @@ def move_activities end def move_events - events = Event.where(originating_user_id: old_account_ids) + events = Event.where(originating_user_id: old_account_id) events.each do |e| e.originating_user_id = account_to_keep.id e.save! @@ -53,7 +53,7 @@ def move_events end def move_comments - comments = Comment.where(user_id: old_account_ids) + comments = Comment.where(user_id: old_account_id) comments.each do |c| c.user_id = account_to_keep.id c.save! @@ -61,13 +61,13 @@ def move_comments end def move_flags - flagging = Flag.where(flagging_user_id: old_account_ids) + flagging = Flag.where(flagging_user_id: old_account_id) flagging.each do |f| f.flagging_user_id = account_to_keep.id f.save! end - resolving = Flag.where(resolving_user_id: old_account_ids) + resolving = Flag.where(resolving_user_id: old_account_id) resolving.each do |f| f.resolving_user_id = account_to_keep.id f.save! @@ -75,7 +75,7 @@ def move_flags end def move_subscriptions - subs = Subscription.where(user_id: old_account_ids) + subs = Subscription.where(user_id: old_account_id) subs.each do |s| if Subscription.where(subscribable: s.subscribable, user_id: account_to_keep.id).exists? s.destroy! @@ -87,7 +87,7 @@ def move_subscriptions end def move_notifications - notified = Notification.where(notified_user_id: old_account_ids) + notified = Notification.where(notified_user_id: old_account_id) notified.each do |n| if Notification.where(event_id: n.event_id, notified_user_id: account_to_keep.id).exists? n.destroy! @@ -97,7 +97,7 @@ def move_notifications end end - notifier = Notification.where(originating_user_id: old_account_ids) + notifier = Notification.where(originating_user_id: old_account_id) notifier.each do |n| if Notification.where(event_id: n.event_id, originating_user_id: account_to_keep.id).exists? n.destroy! @@ -109,7 +109,7 @@ def move_notifications end def move_mentions - mentions = UserMention.where(user_id: old_account_ids) + mentions = UserMention.where(user_id: old_account_id) mentions.each do |m| if UserMention.where(user_id: account_to_keep.id, comment_id: m.comment_id) m.destroy! @@ -121,11 +121,11 @@ def move_mentions end def merge_roles - account_to_keep.role = Role.highest_role_for_users(account_to_keep, *accounts_to_merge) + account_to_keep.role = Role.highest_role_for_users(account_to_keep, account_to_merge) end def move_source_suggestions - suggestions = SourceSuggestion.where(user_id: old_account_ids) + suggestions = SourceSuggestion.where(user_id: old_account_id) suggestions.each do |s| s.user_id = account_to_keep.id s.save! @@ -133,10 +133,8 @@ def move_source_suggestions end def merge_organizations - orgs_to_add = accounts_to_merge.flat_map(&:organizations) - accounts_to_merge.each do |user| - user.organizations = [] - end + orgs_to_add = account_to_merge.organizations + account_to_merge.organizations = [] existing_orgs = account_to_keep.organizations all_orgs = orgs_to_add + existing_orgs account_to_keep.organizations = all_orgs.uniq @@ -144,7 +142,7 @@ def merge_organizations class SuggestedChange < ActiveRecord::Base; end def move_legacy_changes - changes = SuggestedChange.where(user_id: old_account_ids) + changes = SuggestedChange.where(user_id: old_account_id) changes.each do |sc| sc.user_id = account_to_keep.id sc.save! diff --git a/server/app/reports/clingen_counts.rb b/server/app/reports/clingen_counts.rb new file mode 100644 index 000000000..22fdb4d77 --- /dev/null +++ b/server/app/reports/clingen_counts.rb @@ -0,0 +1,58 @@ +class ClingenCounts < Report + attr_reader :start_date, :end_date, :all_org_ids + CLINGEN_ORG_ID = 2 + + def self.name + "ClinGen Contributions" + end + + def self.description + "Count contributions from ClinGen member orgs over a specified timespan." + end + + def self.inputs + { + start_date: :date, + end_date: :date, + include_suborgs: :boolean + } + end + + def setup(start_date:, end_date:, include_suborgs:) + @start_date = Date.parse(start_date) + @end_date = Date.parse(end_date) + + clingen_org = Organization.find(CLINGEN_ORG_ID) + if include_suborgs + sub_groups = clingen_org.groups + @all_org_ids = [clingen_org.id] + sub_groups.map(&:id) + else + @all_org_ids = clingen_org.id + end + end + + def headers + ["Contribution Type", "Count"] + end + + def execute + data << ["Assertions Submitted", SubmitAssertionActivity.where(organization_id: all_org_ids, created_at: (start_date..end_date)).count] + data << ["Evidence Submitted", SubmitEvidenceItemActivity.where(organization_id: all_org_ids, created_at: (start_date..end_date)).count] + data << ["Comments Made", CommentActivity.where(organization_id: all_org_ids, created_at: (start_date..end_date)).count] + data << ["Revisions Suggested", Event.where(organization_id: all_org_ids, action: 'revision suggested', created_at: (start_date..end_date)).count] + data << ["Proposed Revisions Accepted", calculate_contribution('revision suggested', 'revision suggested')] + data << ["Submitted Evidence Accepted", calculate_contribution('accepted', 'submitted')] + data << ["Submitted Assertions Accepted", calculate_contribution('assertion accepted', 'assertion submitted')] + end + + private + def calculate_contribution(submission_event_name, accept_event_name) + contributed_by_clingen = 0 + Event.where(action: accept_event_name, created_at: (start_date..end_date)).find_each do |e| + if Event.where(action: submission_event_name, organization_id: all_org_ids, subject: e.subject).exists? + contributed_by_clingen += 1 + end + end + contributed_by_clingen + end +end diff --git a/server/app/reports/report.rb b/server/app/reports/report.rb new file mode 100644 index 000000000..4a7cde6a9 --- /dev/null +++ b/server/app/reports/report.rb @@ -0,0 +1,70 @@ +class Report + AVAILABLE_REPORTS = [ + ClingenCounts + ] + + def initialize(params) + setup(**params) + rescue => e + errors << e.message + end + + attr_reader :data, :headers, :errors + + def self.name + raise NotImplementedError.new("Specify in subclass") + end + + def self.description + raise NotImplementedError.new("Specify in subclass") + end + + # Can users download this as a TSV + def self.downloadable? + true + end + + # Can users view this directly in the admin UI + def self.viewable? + true + end + + def self.inputs + #format input_name: :type + #supported types :text, :date, :boolean, :int + {} + end + + # Column headers for the report + def headers + raise NotImplementedError.new("Specify in subclass") + end + + # Data rows. #execute should append rows to this list + def data + @data ||= [] + end + + # Append any errors here + def errors + @errors ||= [] + end + + def perform + execute + rescue => e + errors << e.message + end + + # Called from constructor. + # Will receive named arguments from the form inputs + # specified by self.inputs + def setup + raise NotImplementedError.new("Specify in subclass") + end + + # Invoke the report logic. Must set data, headers, or errors + def execute + raise NotImplementedError.new("Specify in subclass") + end +end diff --git a/server/app/utilities/action_wrapper.rb b/server/app/utilities/action_wrapper.rb new file mode 100644 index 000000000..97c6db572 --- /dev/null +++ b/server/app/utilities/action_wrapper.rb @@ -0,0 +1,28 @@ +class ActionWrapper + AVAILABLE_ACTIONS = [ + MergeAccounts + ] + + def self.name + raise NotImplementedError.new("Specify in subclass") + end + + def self.description + raise NotImplementedError.new("Specify in subclass") + end + + def self.inputs + #format input_name: :type + #supported types :text, :date, :boolean, :int + {} + end + + def perform(params) + instance = action_class.new(**params) + instance.perform + end + + def action_class + raise NotImplementedError.new("Implement in Subclass") + end +end diff --git a/server/app/utilities/merge_accounts.rb b/server/app/utilities/merge_accounts.rb new file mode 100644 index 000000000..a8662b258 --- /dev/null +++ b/server/app/utilities/merge_accounts.rb @@ -0,0 +1,21 @@ +class MergeAccounts < ActionWrapper + + def self.name + "Merge User Accounts" + end + + def self.description + "Merge two user accounts, removing one and transfering the authorizations and events to the remaining one." + end + + def self.inputs + { + account_id_to_keep: :int, + account_id_to_merge_in: :int, + } + end + + def action_class + Actions::MergeAccounts + end +end diff --git a/server/app/views/admin/reports/index.html.erb b/server/app/views/admin/reports/index.html.erb new file mode 100644 index 000000000..94f15ab3e --- /dev/null +++ b/server/app/views/admin/reports/index.html.erb @@ -0,0 +1,17 @@ +<%= content_for(:title, "Reports") %> +<%= render "header" %> + +
+
+
+
+
+ <%= table(@reports) do + column :name do |report| + link_to report.name, "/admin/reports/#{report.name}" + end + column :description, truncate: false + end %> +
+
+
diff --git a/server/app/views/admin/reports/result.html.erb b/server/app/views/admin/reports/result.html.erb new file mode 100644 index 000000000..d8d9fe08e --- /dev/null +++ b/server/app/views/admin/reports/result.html.erb @@ -0,0 +1,32 @@ +<%= content_for(:title, @report.name) %> +<%= render "header" %> + +
+
+ Results: +
+
+
+
+ + + + <% @headers.each do |header| %> + + <% end %> + + + + <% @data.each do |row| %> + + <% row.each do |col| %> + + <% end %> + + <% end %> + +
<%= header %>
<%= col %>
+
+
+
+
diff --git a/server/app/views/admin/reports/show.html.erb b/server/app/views/admin/reports/show.html.erb new file mode 100644 index 000000000..efbfa724e --- /dev/null +++ b/server/app/views/admin/reports/show.html.erb @@ -0,0 +1,30 @@ +<%= content_for(:title, @report.name) %> +<%= render "header" %> + +
+ <%= render "trestle/flash/flash" %> +
+ Configure your report: +
+
+
+ <%= form_with do |f| %> + <% @report.inputs.each do |f_name, f_type| %> + <%= render 'admin/shared/field', name: f_name, type: f_type, form: f %> + <% end %> +
+ <%= f.label :format, "Format", class: "control-label" %> +
+ <%= f.radio_button :format, "view", disabled: !@report.viewable?, class: "custom-control-input" %> + <%= f.label :format_view, "View on Web", class: "custom-control-label" %> +
+
+ <%= f.radio_button :format, "download", checked: true, disabled: !@report.downloadable?, class: "custom-control-input" %> + <%= f.label :format_download, "Download TSV", class: "custom-control-label" %> +
+
+ <%= f.submit "Generate Report", class: "btn btn-success" %> + <% end %> +
+
+
diff --git a/server/app/views/admin/shared/_field.html.erb b/server/app/views/admin/shared/_field.html.erb new file mode 100644 index 000000000..4813efb62 --- /dev/null +++ b/server/app/views/admin/shared/_field.html.erb @@ -0,0 +1,13 @@ +<%# locals: (form:, name:, type:) %> +
+ <%= form.label name, class: "control-label" %> + <% if type == :text %> + <%= form.text_field name, required: true %> + <% elsif type == :date %> + <%= form.date_field name, required: true %> + <% elsif type == :boolean %> + <%= form.check_box name, checked: true %> + <% elsif type == :int %> + <%= form.text_field name, required: true, pattern: "\\d+" %> + <% end %> +
diff --git a/server/app/views/admin/utilities/index.html.erb b/server/app/views/admin/utilities/index.html.erb new file mode 100644 index 000000000..445a43894 --- /dev/null +++ b/server/app/views/admin/utilities/index.html.erb @@ -0,0 +1,17 @@ +<%= content_for(:title, "Utilities") %> +<%= render "header" %> + +
+
+
+
+
+ <%= table(@utilities) do + column :name do |util| + link_to util.name, "/admin/utilities/#{util.name}" + end + column :description, truncate: false + end %> +
+
+
diff --git a/server/app/views/admin/utilities/result.html.erb b/server/app/views/admin/utilities/result.html.erb new file mode 100644 index 000000000..d8d9fe08e --- /dev/null +++ b/server/app/views/admin/utilities/result.html.erb @@ -0,0 +1,32 @@ +<%= content_for(:title, @report.name) %> +<%= render "header" %> + +
+
+ Results: +
+
+
+
+ + + + <% @headers.each do |header| %> + + <% end %> + + + + <% @data.each do |row| %> + + <% row.each do |col| %> + + <% end %> + + <% end %> + +
<%= header %>
<%= col %>
+
+
+
+
diff --git a/server/app/views/admin/utilities/show.html.erb b/server/app/views/admin/utilities/show.html.erb new file mode 100644 index 000000000..73693edbd --- /dev/null +++ b/server/app/views/admin/utilities/show.html.erb @@ -0,0 +1,19 @@ +<%= content_for(:title, @util.name) %> +<%= render "header" %> + +
+ <%= render "trestle/flash/flash" %> +
+ Configure your utility: +
+
+
+ <%= form_with do |f| %> + <% @util.inputs.each do |f_name, f_type| %> + <%= render 'admin/shared/field', name: f_name, type: f_type, form: f %> + <% end %> + <%= f.submit @util.name, class: "btn btn-success" %> + <% end %> +
+
+
From 9140d52059d1f434db16a936e68d2c2a29014551 Mon Sep 17 00:00:00 2001 From: Adam Coffman Date: Thu, 18 Apr 2024 20:23:19 -0500 Subject: [PATCH 3/6] fix image uploading --- server/app/admin/organizations_admin.rb | 4 ++++ server/app/admin/users_admin.rb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/server/app/admin/organizations_admin.rb b/server/app/admin/organizations_admin.rb index bcf0d0d6f..98e53e066 100644 --- a/server/app/admin/organizations_admin.rb +++ b/server/app/admin/organizations_admin.rb @@ -1,4 +1,8 @@ Trestle.resource(:organizations) do + controller do + include ActiveStorage::SetCurrent + end + menu do item :organizations, icon: "fa fa-users" end diff --git a/server/app/admin/users_admin.rb b/server/app/admin/users_admin.rb index e2915c223..dd9e5b8e3 100644 --- a/server/app/admin/users_admin.rb +++ b/server/app/admin/users_admin.rb @@ -1,4 +1,8 @@ Trestle.resource(:users) do + controller do + include ActiveStorage::SetCurrent + end + menu do item :users, icon: "fa fa-user" end From 13f5485447e93a3e9346e5183f2c1f71bdb4b7cd Mon Sep 17 00:00:00 2001 From: Adam Coffman Date: Fri, 19 Apr 2024 08:55:21 -0500 Subject: [PATCH 4/6] Update server/app/admin/features_admin.rb Co-authored-by: Susanna Kiwala --- server/app/admin/features_admin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app/admin/features_admin.rb b/server/app/admin/features_admin.rb index 4f6c9f2f9..f692d452f 100644 --- a/server/app/admin/features_admin.rb +++ b/server/app/admin/features_admin.rb @@ -1,6 +1,6 @@ Trestle.resource(:features) do collection do - Fature.includes(:flags).order(name: :asc) + Feature.includes(:flags).order(name: :asc) end search do |q| From a9dacb76076fa7ac765e5ca56c6bb1a84ab9c0c6 Mon Sep 17 00:00:00 2001 From: Adam Coffman Date: Fri, 19 Apr 2024 09:59:42 -0500 Subject: [PATCH 5/6] make org report generic, support select controls --- server/app/admin/reports_admin.rb | 11 +++++-- ...ounts.rb => organization_contributions.rb} | 32 +++++++++---------- server/app/reports/report.rb | 5 +-- server/app/views/admin/shared/_field.html.erb | 4 ++- 4 files changed, 31 insertions(+), 21 deletions(-) rename server/app/reports/{clingen_counts.rb => organization_contributions.rb} (57%) diff --git a/server/app/admin/reports_admin.rb b/server/app/admin/reports_admin.rb index 1b3a9a2e1..d02ab0a60 100644 --- a/server/app/admin/reports_admin.rb +++ b/server/app/admin/reports_admin.rb @@ -14,8 +14,15 @@ def show def generate_report @report = Report::AVAILABLE_REPORTS.find { |x| params[:name] == x.name } - report_params = params.permit(@report.inputs.keys).to_h - report_instance = @report.new(report_params.symbolize_keys) + report_params = params.permit(@report.inputs.keys).to_h.symbolize_keys + #checkbox will come in as 0 or 1 + #cast it to a boolean here so reports dont have to worry about that + report_params.each do |key, val| + if @report.inputs[key] == :boolean + report_params[key] = ActiveRecord::Type::Boolean.new.cast(val) + end + end + report_instance = @report.new(report_params) report_instance.perform unless report_instance.errors.any? if report_instance.errors.any? diff --git a/server/app/reports/clingen_counts.rb b/server/app/reports/organization_contributions.rb similarity index 57% rename from server/app/reports/clingen_counts.rb rename to server/app/reports/organization_contributions.rb index 22fdb4d77..435226ddc 100644 --- a/server/app/reports/clingen_counts.rb +++ b/server/app/reports/organization_contributions.rb @@ -1,33 +1,33 @@ -class ClingenCounts < Report +class OrganizationContributions < Report attr_reader :start_date, :end_date, :all_org_ids - CLINGEN_ORG_ID = 2 def self.name - "ClinGen Contributions" + "Organization Contributions" end def self.description - "Count contributions from ClinGen member orgs over a specified timespan." + "Count contributions from an organization over a specified timespan." end def self.inputs { + organization_id: Organization.order(:name).all.to_a, start_date: :date, end_date: :date, - include_suborgs: :boolean + include_suborgs: :boolean, } end - def setup(start_date:, end_date:, include_suborgs:) + def setup(start_date:, end_date:, include_suborgs:, organization_id:) @start_date = Date.parse(start_date) @end_date = Date.parse(end_date) - clingen_org = Organization.find(CLINGEN_ORG_ID) + org = Organization.find(organization_id) if include_suborgs - sub_groups = clingen_org.groups - @all_org_ids = [clingen_org.id] + sub_groups.map(&:id) + sub_groups = org.groups + @all_org_ids = [org.id] + sub_groups.map(&:id) else - @all_org_ids = clingen_org.id + @all_org_ids = org.id end end @@ -40,19 +40,19 @@ def execute data << ["Evidence Submitted", SubmitEvidenceItemActivity.where(organization_id: all_org_ids, created_at: (start_date..end_date)).count] data << ["Comments Made", CommentActivity.where(organization_id: all_org_ids, created_at: (start_date..end_date)).count] data << ["Revisions Suggested", Event.where(organization_id: all_org_ids, action: 'revision suggested', created_at: (start_date..end_date)).count] - data << ["Proposed Revisions Accepted", calculate_contribution('revision suggested', 'revision suggested')] - data << ["Submitted Evidence Accepted", calculate_contribution('accepted', 'submitted')] - data << ["Submitted Assertions Accepted", calculate_contribution('assertion accepted', 'assertion submitted')] + data << ["Accepted Revisions Proposed by Organization", calculate_contribution('revision suggested', 'revision suggested')] + data << ["Accepted Evidence Submitted by Organization", calculate_contribution('accepted', 'submitted')] + data << ["Accepted Assertions Submitted by Organization", calculate_contribution('assertion accepted', 'assertion submitted')] end private def calculate_contribution(submission_event_name, accept_event_name) - contributed_by_clingen = 0 + contributed_by_org = 0 Event.where(action: accept_event_name, created_at: (start_date..end_date)).find_each do |e| if Event.where(action: submission_event_name, organization_id: all_org_ids, subject: e.subject).exists? - contributed_by_clingen += 1 + contributed_by_org += 1 end end - contributed_by_clingen + contributed_by_org end end diff --git a/server/app/reports/report.rb b/server/app/reports/report.rb index 4a7cde6a9..9905a8073 100644 --- a/server/app/reports/report.rb +++ b/server/app/reports/report.rb @@ -1,6 +1,6 @@ class Report AVAILABLE_REPORTS = [ - ClingenCounts + OrganizationContributions ] def initialize(params) @@ -31,7 +31,8 @@ def self.viewable? def self.inputs #format input_name: :type - #supported types :text, :date, :boolean, :int + #supported primitive types :text, :date, :boolean, :int + #or you can provide an array of objects and a select will be rendered {} end diff --git a/server/app/views/admin/shared/_field.html.erb b/server/app/views/admin/shared/_field.html.erb index 4813efb62..c88d40a44 100644 --- a/server/app/views/admin/shared/_field.html.erb +++ b/server/app/views/admin/shared/_field.html.erb @@ -1,7 +1,9 @@ <%# locals: (form:, name:, type:) %>
<%= form.label name, class: "control-label" %> - <% if type == :text %> + <% if type.is_a?(Array) %> + <%= form.collection_select name, type, :id, :name %> + <% elsif type == :text %> <%= form.text_field name, required: true %> <% elsif type == :date %> <%= form.date_field name, required: true %> From 8a2a80521f04ec02cece61400bf6675a2bdca8d8 Mon Sep 17 00:00:00 2001 From: Adam Coffman Date: Fri, 19 Apr 2024 10:04:33 -0500 Subject: [PATCH 6/6] fix typo --- server/app/admin/features_admin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app/admin/features_admin.rb b/server/app/admin/features_admin.rb index f692d452f..5e74c090f 100644 --- a/server/app/admin/features_admin.rb +++ b/server/app/admin/features_admin.rb @@ -16,7 +16,7 @@ scope :all scope :with_variants, -> { Feature.joins(:variants).distinct }, default: true - scope :flagged, -> { Features.where(flagged: true) } + scope :flagged, -> { Feature.where(flagged: true) } # Customize the table columns shown on the index view. table do