Class: SeoBatchJob
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- SeoBatchJob
- Defined in:
- app/models/seo_batch_job.rb
Overview
Tracks a single batch submission to the Gemini or Anthropic Batch API.
Lifecycle:
collecting -> submitting -> processing -> completed
-> failed
The nightly pipeline creates one SeoBatchJob per run. It progresses through:
- SeoBatchCollectorWorker builds prompts for each page (status: collecting)
- SeoBatchSubmitWorker submits to Batch API (status: submitting -> processing)
- SeoBatchPollWorker monitors until done
- SeoBatchResultsWorker saves results (status: completed)
Provider routing:
- Gemini models -> Gemini Batch API (50% discount)
- Claude models -> Anthropic Message Batches API (50% discount)
== Schema Information
Table name: seo_batch_jobs
Database name: primary
id :bigint not null, primary key
completed_at :datetime
errored_count :integer default(0)
expired_count :integer default(0)
metadata :jsonb
model :string default("claude-sonnet-4-5"), not null
status :string default("collecting"), not null
submitted_at :datetime
succeeded_count :integer default(0)
total_pages :integer default(0)
created_at :datetime not null
updated_at :datetime not null
ai_batch_id :string
Indexes
index_seo_batch_jobs_on_ai_batch_id (ai_batch_id) UNIQUE
index_seo_batch_jobs_on_status (status)
Constant Summary collapse
- STATUSES =
Statuses.
%w[collecting submitting processing completed failed].freeze
Constants included from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
Instance Attribute Summary collapse
- #model ⇒ Object readonly
- #status ⇒ Object readonly
Has many collapse
Class Method Summary collapse
-
.active ⇒ ActiveRecord::Relation<SeoBatchJob>
A relation of SeoBatchJobs that are active.
-
.completed ⇒ ActiveRecord::Relation<SeoBatchJob>
A relation of SeoBatchJobs that are completed.
Instance Method Summary collapse
- #anthropic? ⇒ Boolean
- #collecting? ⇒ Boolean
- #completed? ⇒ Boolean
- #failed? ⇒ Boolean
- #gemini? ⇒ Boolean
- #mark_completed!(succeeded: 0, errored: 0, expired: 0) ⇒ Object
- #mark_failed!(error_message) ⇒ Object
- #mark_submitted!(provider_batch_id:, provider: nil, extra_metadata: {}) ⇒ Object
- #processing? ⇒ Boolean
- #provider_batch_id ⇒ Object
- #submitting? ⇒ Boolean
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#model ⇒ Object (readonly)
50 |
# File 'app/models/seo_batch_job.rb', line 50 validates :model, presence: true |
#status ⇒ Object (readonly)
49 |
# File 'app/models/seo_batch_job.rb', line 49 validates :status, inclusion: { in: STATUSES } |
Class Method Details
.active ⇒ ActiveRecord::Relation<SeoBatchJob>
A relation of SeoBatchJobs that are active. Active Record Scope
52 |
# File 'app/models/seo_batch_job.rb', line 52 scope :active, -> { where(status: %w[collecting submitting processing]) } |
.completed ⇒ ActiveRecord::Relation<SeoBatchJob>
A relation of SeoBatchJobs that are completed. Active Record Scope
53 |
# File 'app/models/seo_batch_job.rb', line 53 scope :completed, -> { where(status: 'completed') } |
Instance Method Details
#anthropic? ⇒ Boolean
62 |
# File 'app/models/seo_batch_job.rb', line 62 def anthropic? = model.to_s.start_with?('claude-') |
#collecting? ⇒ Boolean
55 |
# File 'app/models/seo_batch_job.rb', line 55 def collecting? = status == 'collecting' |
#completed? ⇒ Boolean
58 |
# File 'app/models/seo_batch_job.rb', line 58 def completed? = status == 'completed' |
#failed? ⇒ Boolean
59 |
# File 'app/models/seo_batch_job.rb', line 59 def failed? = status == 'failed' |
#gemini? ⇒ Boolean
61 |
# File 'app/models/seo_batch_job.rb', line 61 def gemini? = model.to_s.start_with?('gemini-') |
#items ⇒ ActiveRecord::Relation<SeoBatchItem>
47 |
# File 'app/models/seo_batch_job.rb', line 47 has_many :items, class_name: 'SeoBatchItem', dependent: :destroy |
#mark_completed!(succeeded: 0, errored: 0, expired: 0) ⇒ Object
82 83 84 85 86 87 88 89 90 |
# File 'app/models/seo_batch_job.rb', line 82 def mark_completed!(succeeded: 0, errored: 0, expired: 0) update!( status: 'completed', succeeded_count: succeeded, errored_count: errored, expired_count: expired, completed_at: Time.current ) end |
#mark_failed!(error_message) ⇒ Object
92 93 94 95 96 97 98 |
# File 'app/models/seo_batch_job.rb', line 92 def mark_failed!() update!( status: 'failed', completed_at: Time.current, metadata: .merge('error' => ) ) end |
#mark_submitted!(provider_batch_id:, provider: nil, extra_metadata: {}) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'app/models/seo_batch_job.rb', line 68 def mark_submitted!(provider_batch_id:, provider: nil, extra_metadata: {}) merged = .merge('provider' => provider || inferred_provider) .merge() update!( status: 'processing', ai_batch_id: provider_batch_id, submitted_at: Time.current, total_pages: items.count, metadata: merged ) end |
#processing? ⇒ Boolean
57 |
# File 'app/models/seo_batch_job.rb', line 57 def processing? = status == 'processing' |
#provider_batch_id ⇒ Object
64 65 66 |
# File 'app/models/seo_batch_job.rb', line 64 def provider_batch_id ai_batch_id end |
#submitting? ⇒ Boolean
56 |
# File 'app/models/seo_batch_job.rb', line 56 def submitting? = status == 'submitting' |