theseus/app/jobs/public/update_map_data_job.rb
2026-01-22 15:55:04 -05:00

136 lines
4.2 KiB
Ruby

class Public::UpdateMapDataJob < ApplicationJob
queue_as :default
good_job_control_concurrency_with(
total_limit: 1,
key: "update_map_data"
)
def perform
map_data = fetch_recent_letters_data
Rails.cache.write("map_data", map_data, expires_in: 1.hour)
map_data
end
private
def fetch_recent_letters_data
the_paranoid_few = Public::User.where(opted_out_of_map: true).pluck(:email)
recent_letters = Letter.joins(:address, :return_address)
.where(
"aasm_state = 'mailed' OR (aasm_state = 'received' AND received_at >= ?)",
7.days.ago
)
.includes(:iv_mtr_events, :address, :return_address)
.where.not(recipient_email: the_paranoid_few)
letters_data = recent_letters.map do |letter|
event_coords = build_letter_event_coordinates(letter)
bubble_title = if letter.aasm_state == "received"
"a letter was received here!"
elsif letter.iv_mtr_events.blank?
"a letter was mailed here!"
else
"a letter was last seen here!"
end
# Find the last valid coordinate (in case the final event failed to geocode)
current_location = event_coords.last
{
coordinates: event_coords,
current_location: current_location,
destination_coords: geocode_destination(letter.address),
bubble_title: bubble_title,
aasm_state: letter.aasm_state,
}
end.select { |letter_data| letter_data[:coordinates].present? }
letters_data
end
def build_letter_event_coordinates(letter)
coordinates = []
# Mailed event coordinates
if letter.mailed_at.present?
coords = geocode_origin(letter.return_address)
coordinates << coords
end
# USPS tracking event coordinates (ordered by scan datetime)
if letter.iv_mtr_events.present?
# Order events by scan datetime to get proper chronological order
ordered_events = letter.iv_mtr_events.sort_by { |event| event.scan_datetime || event.happened_at }
ordered_events.each do |event|
begin
hydrated = event.hydrated
locale_key = hydrated.scan_locale_key
if locale_key.present?
coords = geocode_usps_facility(locale_key, event)
coordinates << coords if coords
end
rescue => e
Rails.logger.warn("Failed to process IV-MTR event #{event.id}: #{e.message}")
# Continue processing other events
end
end
end
# Received event coordinates
if letter.received_at.present?
coords = geocode_destination(letter.address)
coordinates << coords
end
coordinates.compact.reject { |coord| coord.nil? || coord[:lat].nil? || coord[:lon].nil? }
end
def geocode_origin(return_address)
# Special case: anything in Shelburne goes to FIFTEEN_FALLS
if return_address.city&.downcase&.include?("shelburne")
return {
lat: GeocodingService::FIFTEEN_FALLS[:lat].to_f,
lon: GeocodingService::FIFTEEN_FALLS[:lon].to_f,
}
end
# Use non-exact geocoding to avoid doxing
result = GeocodingService.geocode_return_address(return_address, exact: false)
if result && result[:lat] && result[:lon]
{
lat: result[:lat].to_f,
lon: result[:lon].to_f,
}
else
# Fallback to FIFTEEN_FALLS if geocoding fails
{
lat: GeocodingService::FIFTEEN_FALLS[:lat].to_f,
lon: GeocodingService::FIFTEEN_FALLS[:lon].to_f,
}
end
end
def geocode_destination(address)
# Use non-exact geocoding (city only) to avoid doxing
result = GeocodingService.geocode_address_model(address, exact: false)
return nil unless result && result[:lat] && result[:lon]
{
lat: result[:lat].to_f,
lon: result[:lon].to_f,
}
end
def geocode_usps_facility(locale_key, event)
result = GeocodingService::USPSFacilities.coords_for_locale_key(locale_key, event)
return nil unless result && result[:lat] && result[:lon]
{
lat: result[:lat].to_f,
lon: result[:lon].to_f,
}
end
end