Class: AudienceOpenaiExportWorker

Inherits:
Object
  • Object
show all
Includes:
Sidekiq::Job, Sidekiq::Status::Worker
Defined in:
app/workers/audience_openai_export_worker.rb

Overview

On-demand: build the ChatGPT (OpenAI) Ads "Custom Audiences" CSV for an
audience and hand it to the standard file-download flow. Backgrounded because
resolving + hashing a large audience takes ~1 min (198k members ≈ 59s);
progress surfaces in the global Jobs tracker via Sidekiq::Status.

Delivers the file the common way: uploadify the CSV into an Upload and
store upload_id: — JobsController turns that into the download (it sets
session[:download_path] = upload_path(...) so the redirect target auto-starts
the download via the shared _file_downloads bridge). No bespoke attachment
or download action.

OpenAI has no audience-ingest API (its /upload is image-only, verified), so
unlike Google this can't auto-sync; the CSV is uploaded by hand.

Instance Method Summary collapse

Instance Method Details

#perform(audience_id) ⇒ Object

Parameters:

  • audience_id (Integer)


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'app/workers/audience_openai_export_worker.rb', line 23

def perform(audience_id)
  audience = Audience.find_by(id: audience_id)
  return store(info_message: 'Audience not found.') unless audience

  total 1
  at 0, 'Resolving members, hashing identifiers, building the ChatGPT audience files…'

  zip = Marketing::Audiences::OpenaiExport.zip(audience)
  return store(info_message: 'No hashable email/phone identifiers for this audience.') if zip.nil?

  upload = Upload.uploadify_from_data(
    file_name: "audience-#{audience.id}-chatgpt-#{Date.current.iso8601}.zip",
    data: zip, category: 'openai_export', resource: audience
  )
  # Keep ONE export per audience — a retry or re-run replaces the prior file
  # rather than accumulating orphaned Uploads. ponytail: no advisory lock; two
  # genuinely concurrent exports of the same audience (rare for a manual button)
  # could briefly leave two, self-healing on the next run.
  Upload.where(resource: audience, category: 'openai_export').where.not(id: upload.id).destroy_all

  at 1, 'Done'
  store upload_id: upload.id
  store redirect_to: "/audiences/#{audience.id}"
  store complete_message: 'ChatGPT audience files ready — downloading…'
end