Skip to content

Commit

Permalink
Refactor timeline method in AdminGeneralController
Browse files Browse the repository at this point in the history
- Introduce get_timeline_events method for improved timeline event
  fetching
- Improve pagination handling for better performance limiting the amount
  of data loaded into memory
- Optimize SQL queries using UNION ALL for combined event types
- Separate logic for authority changes and info request events
- Remove redundant code and simplify overall structure

Fixes #8369
  • Loading branch information
gbp committed Nov 19, 2024
1 parent 63bba9f commit d9116aa
Showing 1 changed file with 45 additions and 54 deletions.
99 changes: 45 additions & 54 deletions app/controllers/admin_general_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,50 +103,14 @@ def timeline
@events_title = get_events_title

@events = WillPaginate::Collection.create((params[:page] or 1), 100) do |pager|
# create a hash for each model type being returned
info_request_event_ids = {}
public_body_version_ids = {}
comment_ids = {}
# get the relevant slice from the paginator
timestamps = get_timestamps
timestamps.slice(pager.offset, pager.per_page).each_with_index do |event, index|
# for each event in the slice, add an item to the hash for the model type
# whose key is the model id, and value is the position in the slice
if event[1] == 'InfoRequestEvent'
info_request_event_ids[event[0].to_i] = index
elsif event[1] == 'Comment'
comment_ids[event[0].to_i] = index
else
public_body_version_ids[event[0].to_i] = index
end
end
# get all the models in the slice, eagerly loading the associations we use in the view
public_body_versions = PublicBody.versioned_class.
includes(public_body: :translations).
find(public_body_version_ids.keys)
info_request_events = InfoRequestEvent.
includes(:info_request).
find(info_request_event_ids.keys)
comments = Comment.where(event_type: 'destroy_comment').
includes(:info_request).
find(comment_ids.keys)
@events = []
# drop the models into a combined array, ordered by their position in the timestamp slice
public_body_versions.each do |version|
@events[public_body_version_ids[version.id]] = [version, version.updated_at]
end
info_request_events.each do |event|
@events[info_request_event_ids[event.id]] = [event, event.created_at]
end
comments.each do |comment|
@events[comment_ids[comment.id]] = [comment, comment.created_at]
end
events, count = get_timeline_events(pager)

# inject the result array into the paginated collection:
pager.replace(@events)
pager.replace(events)

# set the total entries for the page to the overall number of results
pager.total_entries = timestamps.size
pager.total_entries = count
end
end

Expand Down Expand Up @@ -186,33 +150,60 @@ def get_events_title
end
end

def get_timestamps
# Get an array of event attributes within the timespan in the format
# [id, type_of_model, event_timestamp]
def get_timeline_events(pager)
# Get an array of events, timestamps and total record count within the
# timespan in the selected event type
#
# Note that the relevant date for InfoRequestEvents is creation, but
# for PublicBodyVersions is update throughout
connection = InfoRequestEvent.connection

authority_change_scope = PublicBody.versioned_class.
select("id, 'PublicBodyVersion', updated_at AS timestamp").
select("id, 'PublicBodyVersion' as type, updated_at AS timestamp").
where(updated_at: start_date...).
order(timestamp: :desc)
order(timestamp: :desc).
limit(pager.per_page).
offset(pager.offset)
authority_change_count = authority_change_scope.
unscope(:select, :order, :limit, :offset).
count

info_request_event_scope = InfoRequestEvent.
select("id, 'InfoRequestEvent', created_at AS timestamp").
select("id, 'InfoRequestEvent' as type, created_at AS timestamp").
where(created_at: start_date...).
order(timestamp: :desc)
order(timestamp: :desc).
limit(pager.per_page).
offset(pager.offset)
info_request_event_count = info_request_event_scope.
unscope(:select, :order, :limit, :offset).
count

case params[:event_type]
when 'authority_change'
connection.select_rows(authority_change_scope.to_sql)
count = authority_change_count
scope = authority_change_scope
when 'info_request_event'
connection.select_rows(info_request_event_scope.to_sql)
count = info_request_event_count
scope = info_request_event_scope
else
connection.select_rows("#{info_request_event_scope.unscope(:order).to_sql}
UNION
#{authority_change_scope.unscope(:order).to_sql}
ORDER by timestamp desc")
count = authority_change_count + info_request_event_count
sql_query = <<~SQL
(#{authority_change_scope.unscope(:order, :limit, :offset).to_sql})
UNION ALL
(#{info_request_event_scope.unscope(:order, :limit, :offset).to_sql})
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
SQL
full_sql_query = ActiveRecord::Base.send(
:sanitize_sql_array, [
sql_query, limit: pager.per_page, offset: pager.offset
]
)
scope = ActiveRecord::Base.connection.execute(full_sql_query)
end

records_and_timestamps = scope.map do |record|
[record['type'].constantize.find(record['id']), record['timestamp']]
end

[records_and_timestamps, count]
end
end

0 comments on commit d9116aa

Please sign in to comment.