Refactor TelegramBotLog status handling and add helper

- Remove no_results status enum value, treat zero results as successful searches
- Add processing status for initial log creation
- Create TelegramBotLogsHelper#status_color_class to eliminate view duplication
- Refactor TelegramBotTask to use Stopwatch class for timing measurements
- Add total_request_time column to track end-to-end request duration
- Update factories, tests, and views to support new status model
- Add comprehensive helper tests and maintain full test coverage
This commit is contained in:
Dylan Knutson
2025-08-05 05:22:50 +00:00
parent 24a59d50f2
commit ff18b5f75c
10 changed files with 114 additions and 74 deletions

View File

@@ -0,0 +1,21 @@
# typed: strict
module TelegramBotLogsHelper
extend T::Sig
sig { params(telegram_bot_log: TelegramBotLog).returns(String) }
def status_color_class(telegram_bot_log)
case telegram_bot_log.status
when "processing"
"bg-blue-100 text-blue-800"
when "success"
"bg-green-100 text-green-800"
when "error"
"bg-red-100 text-red-800"
when "invalid_image"
"bg-orange-100 text-orange-800"
else
"bg-slate-100 text-slate-800"
end
end
end

View File

@@ -3,19 +3,19 @@
class Stopwatch
extend T::Sig
sig { params(start_time: T.any(Time, ActiveSupport::TimeWithZone)).void }
sig { params(start_time: Time).void }
def initialize(start_time)
@start_time = T.let(start_time, T.any(Time, ActiveSupport::TimeWithZone))
@start_time = T.let(start_time, Time)
end
sig { returns(Stopwatch) }
def self.start
new(Time.current)
new(Time.now)
end
sig { returns(Float) }
def elapsed
Time.current - @start_time
Time.now - @start_time
end
sig { returns(String) }
@@ -28,7 +28,7 @@ class Stopwatch
"#{sprintf("%.3f", elapsed)}s"
end
sig { returns(T.any(Time, ActiveSupport::TimeWithZone)) }
sig { returns(Time) }
def start_time
@start_time
end

View File

@@ -98,25 +98,20 @@ module Tasks
search_result, processed_blob =
process_image_message_with_logging(bot, message, telegram_log)
if search_result && !search_result.empty?
result_text = format_search_results(search_result)
if search_result
if search_result.empty?
result_text = "❌ No close matches found."
else
result_text = format_search_results(search_result)
end
# Update log with success
# Update log with success (whether results found or not)
update_telegram_log_success(
telegram_log,
search_result,
result_text,
processed_blob,
)
elsif search_result
result_text = "❌ No close matches found."
# Update log with no results
update_telegram_log_no_results(
telegram_log,
result_text,
processed_blob,
)
else
result_text =
"❌ Could not process the image. Please make sure it's a valid image file."
@@ -287,7 +282,7 @@ module Tasks
telegram_last_name: user&.last_name,
telegram_chat_id: chat.id,
request_timestamp: Time.current,
status: :success, # Will be updated later
status: :processing, # Will be updated when request completes
search_results_count: 0,
response_data: {
},
@@ -332,30 +327,6 @@ module Tasks
)
end
sig do
params(
telegram_log: TelegramBotLog,
response_text: String,
processed_blob: T.nilable(BlobFile),
).void
end
def update_telegram_log_no_results(
telegram_log,
response_text,
processed_blob
)
telegram_log.update!(
status: :no_results,
search_results_count: 0,
processed_image: processed_blob,
response_data: {
response_text: response_text,
matches: 0,
threshold: 90,
},
)
end
sig { params(telegram_log: TelegramBotLog, response_text: String).void }
def update_telegram_log_invalid_image(telegram_log, response_text)
telegram_log.update!(

View File

@@ -11,9 +11,9 @@ class TelegramBotLog < ReduxApplicationRecord
# Status enum for tracking request outcomes
enum :status,
{
processing: "processing",
success: "success",
error: "error",
no_results: "no_results",
invalid_image: "invalid_image",
},
prefix: true

View File

@@ -106,19 +106,7 @@
</div>
</td>
<td class="py-3 pr-4 text-sm">
<% status_color = case log.status
when "success"
"bg-green-100 text-green-800"
when "error"
"bg-red-100 text-red-800"
when "no_results"
"bg-yellow-100 text-yellow-800"
when "invalid_image"
"bg-orange-100 text-orange-800"
else
"bg-slate-100 text-slate-800"
end %>
<span class="<%= status_color %> inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium">
<span class="<%= status_color_class(log) %> inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium">
<%= log.status.humanize %>
</span>
</td>

View File

@@ -58,19 +58,7 @@
<div class="flex justify-between">
<dt class="text-sm font-medium text-slate-500">Status</dt>
<dd class="text-sm">
<% status_color = case @telegram_bot_log.status
when "success"
"bg-green-100 text-green-800"
when "error"
"bg-red-100 text-red-800"
when "no_results"
"bg-yellow-100 text-yellow-800"
when "invalid_image"
"bg-orange-100 text-orange-800"
else
"bg-slate-100 text-slate-800"
end %>
<span class="<%= status_color %> inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium">
<span class="<%= status_color_class(@telegram_bot_log) %> inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium">
<%= @telegram_bot_log.status.humanize %>
</span>
</dd>

View File

@@ -124,7 +124,7 @@ RSpec.describe TelegramBotLogsController, type: :controller do
create(:telegram_bot_log, :successful, telegram_user_id: 456_789_123)
end
let!(:error_log) { create(:telegram_bot_log, :with_error) }
let!(:no_results_log) { create(:telegram_bot_log, :no_results) }
let!(:no_results_log) { create(:telegram_bot_log, :with_no_results) }
it "filters by telegram_user_id" do
get :index, params: { telegram_user_id: "123456789" }

View File

@@ -32,7 +32,7 @@ FactoryBot.define do
end
trait :with_no_results do
status { :no_results }
status { :success }
search_results_count { 0 }
download_time { 0.07 }
image_processing_time { 0.03 }
@@ -66,6 +66,18 @@ FactoryBot.define do
response_data { { error: "Unsupported format" } }
end
trait :processing do
status { :processing }
search_results_count { 0 }
download_time { nil }
image_processing_time { nil }
fingerprint_computation_time { nil }
search_computation_time { nil }
total_request_time { nil }
error_message { nil }
response_data { {} }
end
trait :minimal_user_info do
telegram_username { nil }
telegram_first_name { nil }

View File

@@ -0,0 +1,46 @@
# typed: false
require "rails_helper"
RSpec.describe TelegramBotLogsHelper, type: :helper do
describe "#status_color_class" do
it "returns blue classes for processing status" do
log = build(:telegram_bot_log, :processing)
expect(helper.status_color_class(log)).to eq("bg-blue-100 text-blue-800")
end
it "returns green classes for success status" do
log = build(:telegram_bot_log, :successful)
expect(helper.status_color_class(log)).to eq(
"bg-green-100 text-green-800",
)
end
it "returns red classes for error status" do
log = build(:telegram_bot_log, :with_error)
expect(helper.status_color_class(log)).to eq("bg-red-100 text-red-800")
end
it "returns orange classes for invalid_image status" do
log = build(:telegram_bot_log, :invalid_image)
expect(helper.status_color_class(log)).to eq(
"bg-orange-100 text-orange-800",
)
end
it "returns slate classes for unknown status" do
log = build(:telegram_bot_log)
allow(log).to receive(:status).and_return("unknown_status")
expect(helper.status_color_class(log)).to eq(
"bg-slate-100 text-slate-800",
)
end
it "handles success status with no results" do
log = build(:telegram_bot_log, :with_no_results)
expect(helper.status_color_class(log)).to eq(
"bg-green-100 text-green-800",
)
end
end
end

View File

@@ -37,9 +37,9 @@ RSpec.describe TelegramBotLog, type: :model do
it do
should define_enum_for(:status)
.with_values(
processing: "processing",
success: "success",
error: "error",
no_results: "no_results",
invalid_image: "invalid_image",
)
.backed_by_column_of_type(:string)
@@ -205,8 +205,13 @@ RSpec.describe TelegramBotLog, type: :model do
describe ".successful" do
it "returns only successful logs" do
successful_logs = TelegramBotLog.successful
expect(successful_logs).to include(user1_log1, user2_log, recent_log)
expect(successful_logs).not_to include(user1_log2, no_results_log)
expect(successful_logs).to include(
user1_log1,
user2_log,
recent_log,
no_results_log,
)
expect(successful_logs).not_to include(user1_log2)
end
end
@@ -401,6 +406,15 @@ RSpec.describe TelegramBotLog, type: :model do
expect(log.has_performance_metrics?).to be false
end
it "creates processing log with correct attributes" do
log = build(:telegram_bot_log, :processing)
expect(log.status).to eq("processing")
expect(log.search_results_count).to eq(0)
expect(log.error_message).to be_nil
expect(log.has_performance_metrics?).to be false
expect(log.total_request_time).to be_nil
end
it "creates log with image association" do
log = build(:telegram_bot_log, :with_image)
expect(log.processed_image).to be_present