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..f692d452f 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)
+ Feature.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/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/reports_admin.rb b/server/app/admin/reports_admin.rb
new file mode 100644
index 000000000..d02ab0a60
--- /dev/null
+++ b/server/app/admin/reports_admin.rb
@@ -0,0 +1,65 @@
+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.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?
+ 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/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
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/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/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/organization_contributions.rb b/server/app/reports/organization_contributions.rb
new file mode 100644
index 000000000..435226ddc
--- /dev/null
+++ b/server/app/reports/organization_contributions.rb
@@ -0,0 +1,58 @@
+class OrganizationContributions < Report
+ attr_reader :start_date, :end_date, :all_org_ids
+
+ def self.name
+ "Organization Contributions"
+ end
+
+ def self.description
+ "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,
+ }
+ end
+
+ def setup(start_date:, end_date:, include_suborgs:, organization_id:)
+ @start_date = Date.parse(start_date)
+ @end_date = Date.parse(end_date)
+
+ org = Organization.find(organization_id)
+ if include_suborgs
+ sub_groups = org.groups
+ @all_org_ids = [org.id] + sub_groups.map(&:id)
+ else
+ @all_org_ids = 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 << ["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_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_org += 1
+ end
+ end
+ contributed_by_org
+ end
+end
diff --git a/server/app/reports/report.rb b/server/app/reports/report.rb
new file mode 100644
index 000000000..9905a8073
--- /dev/null
+++ b/server/app/reports/report.rb
@@ -0,0 +1,71 @@
+class Report
+ AVAILABLE_REPORTS = [
+ OrganizationContributions
+ ]
+
+ 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 primitive types :text, :date, :boolean, :int
+ #or you can provide an array of objects and a select will be rendered
+ {}
+ 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| %>
+ <%= header %> |
+ <% end %>
+
+
+
+ <% @data.each do |row| %>
+
+ <% row.each do |col| %>
+ <%= col %> |
+ <% end %>
+
+ <% end %>
+
+
+
+
+
+
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.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..c88d40a44
--- /dev/null
+++ b/server/app/views/admin/shared/_field.html.erb
@@ -0,0 +1,15 @@
+<%# locals: (form:, name:, type:) %>
+
+ <%= form.label name, class: "control-label" %>
+ <% 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 %>
+ <% 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| %>
+ <%= header %> |
+ <% end %>
+
+
+
+ <% @data.each do |row| %>
+
+ <% row.each do |col| %>
+ <%= col %> |
+ <% end %>
+
+ <% end %>
+
+
+
+
+
+
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 %>
+
+
+
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|