Skip to content

Commit

Permalink
mark a submission as spam
Browse files Browse the repository at this point in the history
Generally, moves the Form Responses inbox more toward an email folder, to encourage responsiveness to feedback, rather than merely counting feedback for reporting purposes.

* ensure actions are top aligned in a row
* make `archived` a flag, rather than a Submission state
* default scope excludes flagged, archived, deleted, and includes archived
* show time for current day respones, month and day for older responses this year, and MM/DD/YYYY for responses from last calendar year or prior
* preview the contents of a submission
* implements soft-delete for responses (responses can be moved to Trash)
* update locales to rails defaults
* restyle and refactor pagination
* change submissions cursor to a pointer
  • Loading branch information
ryanwoldatwork authored Feb 14, 2025
1 parent 97364f5 commit d57b856
Show file tree
Hide file tree
Showing 59 changed files with 783 additions and 569 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ group :development do
gem "brakeman"
gem 'bullet'
gem "bundler-audit"
gem 'faker', git: 'https://github.com/faker-ruby/faker.git', branch: 'main'
gem 'listen'
gem 'rails-erd'
gem "rubocop-rails"
Expand Down
9 changes: 9 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ GIT
multi_json (~> 1.14)
omniauth (~> 2.0)

GIT
remote: https://github.com/faker-ruby/faker.git
revision: 3a65e1e2e567cb3be3f6b9582484ba4d5ee5d8c6
branch: main
specs:
faker (3.5.1)
i18n (>= 1.8.11, < 2)

GEM
remote: https://rubygems.org/
specs:
Expand Down Expand Up @@ -660,6 +668,7 @@ DEPENDENCIES
devise (>= 4.8.1)
dotenv
factory_bot_rails
faker!
fog-aws (>= 3.15.0)
image_processing (~> 1.12)
importmap-rails (>= 2.0.0)
Expand Down
18 changes: 6 additions & 12 deletions app/assets/stylesheets/site.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,10 @@ footer {
}

// For the extra-wide Responses table
.table-scroll {
position:relative;
max-width: 100%;
margin: auto;
}
.table-wrap {
width:100%;
overflow:auto;
}
.table-scroll table {
width:100%;
}
.table-scroll th,
.table-scroll td {
max-width: 600px;
}

.dark-blue-bg {
background-color: #162D50;
Expand Down Expand Up @@ -617,4 +605,10 @@ abbr[title=required] {
z-index: 1000;
padding: 10px;
border-bottom: 1px solid #ccc;
}

.usa-table.submissions tbody tr:hover,
.usa-table.submissions tbody tr:hover td {
cursor: pointer !important;

}
23 changes: 8 additions & 15 deletions app/controllers/admin/forms_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class FormsController < AdminController
archive
reset
add_tag remove_tag
update_ui_truncation
update_title update_instructions update_disclaimer_text
update_success_text update_display_logo
update_notification_emails
Expand Down Expand Up @@ -187,7 +186,8 @@ def export
start_date = params[:start_date] ? Date.parse(params[:start_date]).to_date.beginning_of_day : Time.zone.now.beginning_of_quarter
end_date = params[:end_date] ? Date.parse(params[:end_date]).to_date.end_of_day : Time.zone.now.end_of_quarter

count = Form.find_by_short_uuid(@form.short_uuid).non_flagged_submissions(start_date:, end_date:).count
count = Form.find_by_short_uuid(@form.short_uuid)
.reportable_submissions(start_date:, end_date:).count
if count > MAX_ROWS_TO_EXPORT
render status: :bad_request, plain: "Your response set contains #{helpers.number_with_delimiter count} responses and is too big to be exported from the Touchpoints app. Consider using the Touchpoints API to download large response sets (over #{helpers.number_with_delimiter MAX_ROWS_TO_EXPORT} responses)."
return
Expand Down Expand Up @@ -223,6 +223,8 @@ def questions
end

def responses
@search_params = search_params
@search_params.merge!(form_id: @form.short_uuid)
FormCache.invalidate_reports(@form.short_uuid) if params['use_cache'].present? && params['use_cache'] == 'false'
ensure_response_viewer(form: @form) unless @form.template?
end
Expand Down Expand Up @@ -315,18 +317,6 @@ def copy_by_id
copy
end

def update_ui_truncation
ensure_response_viewer(form: @form)

respond_to do |format|
if @form.update(ui_truncate_text_responses: !@form.ui_truncate_text_responses)
format.json { render json: {}, status: :ok, location: @form }
else
format.json { render json: @form.errors, status: :unprocessable_entity }
end
end
end

def update
ensure_form_manager(form: @form)

Expand Down Expand Up @@ -537,7 +527,6 @@ def form_params
:load_css,
:tag_list,
:verify_csrf,
:ui_truncate_text_responses,
:question_text_01,
:question_text_02,
:question_text_03,
Expand Down Expand Up @@ -610,5 +599,9 @@ def transition_state
def invite_params
params.require(:user).permit(:refer_user)
end

def search_params
params.permit(:form_id, :flagged, :spam, :archived, :deleted)
end
end
end
4 changes: 3 additions & 1 deletion app/controllers/admin/reporting_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ def hisps
end

def lifespan
@form_lifespans = Submission.select('form_id, count(*) as num_submissions, (max(submissions.created_at) - min(submissions.created_at)) as lifespan').group(:form_id)
@form_lifespans = Submission
.select('form_id, count(*) as num_submissions, (max(submissions.created_at) - min(submissions.created_at)) as lifespan')
.group("form_id")
@forms = Form.select(:id, :name, :organization_id, :uuid, :short_uuid).where('exists (select id from submissions where submissions.form_id = forms.id)')
@orgs = Organization.order(:name)
@org_summary = []
Expand Down
130 changes: 106 additions & 24 deletions app/controllers/admin/submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ def update
end

def search
@all_submissions = @form.submissions
@all_submissions = @form.submissions.ordered
@all_submissions = @all_submissions.where(":tags = ANY (tags)", tags: params[:tag]) if params[:tag]
if params[:archived]
@submissions = @all_submissions.order('submissions.created_at DESC').page params[:page]
@submissions = @all_submissions.page params[:page]
else
@submissions = @all_submissions.non_archived.order('submissions.created_at DESC').page params[:page]
@submissions = @all_submissions.active.page params[:page]
end
end

Expand Down Expand Up @@ -92,7 +92,13 @@ def a11_chart

def responses_per_day
@dates = (45.days.ago.to_date..Date.today).map { |date| date }
@response_groups = @form.submissions.where("created_at >= ?", 45.days.ago).group('date(created_at)').size.sort.last(45)

@response_groups = Submission
.where(form_id: @form.id)
.where("created_at >= ?", 45.days.ago)
.group(Arel.sql("DATE(created_at)"))
.count.sort

# Add in 0 count days to fetched analytics
@dates.each do |date|
@response_groups << [date, 0] unless @response_groups.detect { |row| row[0].strftime('%m %d %Y') == date.strftime('%m %d %Y') }
Expand All @@ -101,9 +107,19 @@ def responses_per_day
end

def responses_by_status
responses_by_aasm = @form.submissions.group(:aasm_state).count
flagged_count = @form.submissions.where(flagged: true).size
@responses_by_status = { **responses_by_aasm, 'flagged' => flagged_count, 'total' => responses_by_aasm.values.sum }
form_submissions = @form.submissions

responses_by_aasm = form_submissions.group(:aasm_state).count
flagged_count = form_submissions.flagged.count
archived_count = form_submissions.archived.count
marked_count = form_submissions.marked_as_spam.count
deleted_count = form_submissions.deleted.count
@responses_by_status = { **responses_by_aasm,
'flagged' => flagged_count,
'marked' => marked_count,
'archived' => archived_count,
'deleted' => deleted_count,
'total' => responses_by_aasm.values.sum }
@responses_by_status.default = 0
end

Expand All @@ -112,41 +128,88 @@ def performance_gov
end

def submissions_table
@show_archived = true if params[:archived]
all_submissions = @form.submissions
all_submissions = all_submissions.where(":tags = ANY (tags)", tags: params[:tag]) if params[:tag]
if params[:archived]
@submissions = all_submissions.order('submissions.created_at DESC').page params[:page]
@show_flagged = search_params[:flagged] == "1"
@show_marked_as_spam = search_params[:spam] == "1"
@show_archived = search_params[:archived] == "1"
@show_deleted = search_params[:deleted] == "1"

@submissions = @form.submissions

# Apply filters based on query params
if search_params[:tag]
@submissions = @submissions.where(":tags = ANY (tags)", tags: search_params[:tag])
end

if @show_flagged
@submissions = @submissions.flagged
elsif @show_marked_as_spam
@submissions = @submissions.marked_as_spam
elsif @show_archived
@submissions = @submissions.archived
elsif @show_deleted
@submissions = @submissions.deleted
elsif @show_deleted
@submissions = @submissions.deleted
else
@submissions = all_submissions.non_archived.order('submissions.created_at DESC').page params[:page]
@submissions = @submissions.active
end

@submissions = @submissions.ordered.page(params[:page])
end

def archive
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_archived], 'Submission', @submission.id, "Submission #{@submission.id} archived at #{DateTime.now}", current_user.id)
@submission.archive_without_validation!
@submission.update_attribute(:archived, true)
end

def unarchive
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_unarchived], 'Submission', @submission.id, "Submission #{@submission.id} unarchived at #{DateTime.now}", current_user.id)
@submission.reset_without_validation!
@submission.update_attribute(:archived, false)
end

def mark
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_marked_as_spam], 'Submission', @submission.id, "Submission #{@submission.id} marked as spam at #{DateTime.now}", current_user.id)
@submission.update_attribute(:spam, true)
end

def unmark
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_unmarked_as_spam], 'Submission', @submission.id, "Submission #{@submission.id} unmarked as spam at #{DateTime.now}", current_user.id)
@submission.update_attribute(:spam, false)
end

def delete
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} undeleted at #{DateTime.now}", current_user.id)
@submission.update(deleted: true, deleted_at: Time.now)
end

def destroy
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} deleted at #{DateTime.now}", current_user.id)
Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} undeleted at #{DateTime.now}", current_user.id)
@submission.update(deleted: true, deleted_at: Time.now)

@submission.destroy
respond_to do |format|
format.js { render :destroy }
end
end

def undelete
ensure_form_manager(form: @form)

Event.log_event(Event.names[:response_undeleted], 'Submission', @submission.id, "Submission #{@submission.id} deleted at #{DateTime.now}", current_user.id)
@submission.update(deleted: false, deleted_at: nil)
end

def feed
@days_limit = (params[:days_limit].present? ? params[:days_limit].to_i : 1)
@feed = get_feed_data(@days_limit)
Expand Down Expand Up @@ -178,7 +241,7 @@ def get_feed_data(days_limit)
all_question_responses = []

Form.all.each do |form|
submissions = form.submissions
submissions = form.submissions.ordered
submissions = submissions.where('created_at >= ?', days_limit.days.ago) if days_limit.positive?
submissions.each do |submission|
form.ordered_questions.each do |question|
Expand Down Expand Up @@ -207,30 +270,38 @@ def get_feed_data(days_limit)

def bulk_update
submission_ids = params[:submission_ids] # Array of selected submission_ids
bulk_action = params[:bulk_action] # The selected action ('flag' or 'archive')
bulk_action = params[:bulk_action] # The selected action ('flag', 'archive', or 'spam')

if submission_ids.present?
submissions = @form.submissions.where(id: submission_ids)
submissions = @form.submissions
.where(id: submission_ids)
.ordered

case bulk_action
when 'archive'
submissions.each do |submission|
Event.log_event(Event.names[:response_archived], 'Submission', submission.id, "Submission #{submission.id} archived at #{DateTime.now}", current_user.id)
submission.archive_without_validation!
submission.update_attribute(:archived, true)
end
flash[:notice] = "#{submissions.count} Submissions archived."
flash[:notice] = "#{view_context.pluralize(submissions.count, 'Submission')} archived."
when 'flag'
submissions.each do |submission|
Event.log_event(Event.names[:response_flagged], 'Submission', submission.id, "Submission #{submission.id} flagged at #{DateTime.now}", current_user.id)
submission.update_attribute(:flagged, true)
end
flash[:notice] = "#{submissions.count} Submissions flagged."
flash[:notice] = "#{view_context.pluralize(submissions.count, 'Submission')} flagged."
when 'spam'
submissions.each do |submission|
Event.log_event(Event.names[:response_marked_as_spam], 'Submission', submission.id, "Submission #{submission.id} marked as spam at #{DateTime.now}", current_user.id)
submission.update_attribute(:spam, true)
end
flash[:notice] = "#{submissions.count} Submissions marked as spam."
flash[:notice] = "#{view_context.pluralize(submissions.count, 'Submission')} marked as spam."
when 'delete'
submissions.each do |submission|
Event.log_event(Event.names[:response_deleted], 'Submission', submission.id, "Submission #{submission.id} deleted at #{DateTime.now}", current_user.id)
submission.update(deleted: true, deleted_at: Time.now)
end
flash[:notice] = "#{view_context.pluralize(submissions.count, 'Submission')} deleted."
else
flash[:alert] = "Invalid action selected."
end
Expand Down Expand Up @@ -273,5 +344,16 @@ def status_params
def tag_params
params.require(:submission).permit(:tag)
end

def search_params
params.permit(
:form_id,
:flagged,
:spam,
:archived,
:deleted,
:tags,
)
end
end
end
14 changes: 14 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,20 @@ def format_time(time, timezone)
I18n.l time.to_time.in_time_zone(timezone), format: :with_timezone
end

def format_submission_time(datetime, time_zone)
created_at = datetime.in_time_zone(time_zone)
today = Date.today.in_time_zone(time_zone).beginning_of_day
start_of_year = today.beginning_of_year

if created_at >= today
created_at.strftime("%-I:%M %p") # Today: 1:23 PM
elsif created_at >= start_of_year
created_at.strftime("%b %e") # Current year: Jan 5
else
created_at.strftime("%m/%d/%Y") # Last year: 01/05/2024
end
end

def timezone_abbreviation(timezone)
zone = ActiveSupport::TimeZone.new(timezone)
zone.now.strftime('%Z')
Expand Down
2 changes: 1 addition & 1 deletion app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def submissions_digest(form_id, days_ago)
return unless @form.send_notifications?

set_logo
@submissions = Submission.where(id: form_id).where('created_at > ?', @begin_day).order('created_at desc')
@submissions = @form.submissions.where('created_at > ?', @begin_day)
return unless @submissions.present?

emails = @form.notification_emails.split(',')
Expand Down
Loading

0 comments on commit d57b856

Please sign in to comment.