Class: Mensa::Export

Inherits:
ApplicationRecord show all
Defined in:
app/models/mensa/export.rb

Overview

An export request for a Mensa table. Each export captures the table it was generated for, the view (if any), the requesting user and the request configuration (filters/query/order/page) needed to rebuild the data. Once processed by Mensa::ExportJob the generated CSV is stored in asset.

Constant Summary collapse

STATUSES =
%w[pending processing completed failed].freeze
FORMATS =
%w[csv_excel plain_csv].freeze
SCOPES =
%w[all current_page].freeze
REPEATS =
["", "daily", "weekly", "monthly", "quarterly", "bi-yearly", "yearly"].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.badge_dom_id(table_name, user) ⇒ Object



113
114
115
# File 'app/models/mensa/export.rb', line 113

def self.badge_dom_id(table_name, user)
  "mensa-export-badge-#{token(table_name, user)}"
end

.broadcast_refresh(table_name, user) ⇒ Object

Re-renders the export button badge and downloads list for everyone subscribed to this table/user’s export stream. Best-effort: a missing Action Cable backend (or other broadcast failure) must never break the caller (job completion, download cleanup, …).



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'app/models/mensa/export.rb', line 125

def self.broadcast_refresh(table_name, user)
  stream = stream_name(table_name, user)

  Turbo::StreamsChannel.broadcast_replace_to(
    stream,
    target: badge_dom_id(table_name, user),
    partial: "mensa/exports/badge",
    locals: {table_name: table_name, user: user}
  )

  Turbo::StreamsChannel.broadcast_replace_to(
    stream,
    target: list_dom_id(table_name, user),
    partial: "mensa/exports/list",
    locals: {table_name: table_name, user: user, exports: for_table(table_name).for_user(user).recent}
  )
rescue => e
  Mensa.config.logger&.warn("Mensa::Export broadcast failed: #{e.class}: #{e.message}")
end

.completed_count(table_name, user) ⇒ Object

Number of currently-downloadable exports for a table/user combination. This is the number rendered in the export button badge.



97
98
99
# File 'app/models/mensa/export.rb', line 97

def self.completed_count(table_name, user)
  for_table(table_name).for_user(user).with_downloadable_asset.count
end

.list_dom_id(table_name, user) ⇒ Object



117
118
119
# File 'app/models/mensa/export.rb', line 117

def self.list_dom_id(table_name, user)
  "mensa-export-list-#{token(table_name, user)}"
end

.stream_name(table_name, user) ⇒ Object



109
110
111
# File 'app/models/mensa/export.rb', line 109

def self.stream_name(table_name, user)
  "mensa-exports-#{token(table_name, user)}"
end

.token(table_name, user) ⇒ Object

A stable, page-independent key identifying the exports of a table/user combination, used for Turbo stream names and DOM ids so background jobs can target them after completion.



104
105
106
107
# File 'app/models/mensa/export.rb', line 104

def self.token(table_name, user)
  user_key = user.respond_to?(:id) ? user&.id : user
  [table_name.to_s, user_key || "anonymous"].join("-").parameterize
end

Instance Method Details

#completed?Boolean

Returns:

  • (Boolean)


26
27
28
# File 'app/models/mensa/export.rb', line 26

def completed?
  status == "completed"
end

#downloadable?Boolean

True once the asset is ready to be downloaded by the user.

Returns:

  • (Boolean)


91
92
93
# File 'app/models/mensa/export.rb', line 91

def downloadable?
  completed? && asset.attached?
end

#failed?Boolean

Returns:

  • (Boolean)


30
31
32
# File 'app/models/mensa/export.rb', line 30

def failed?
  status == "failed"
end

#next_repeat_run_at(from: nil) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'app/models/mensa/export.rb', line 42

def next_repeat_run_at(from: nil)
  return if repeat.blank?

  anchor = from || last_repeat_run_at || created_at

  case repeat
  when "daily"
    anchor + 1.day
  when "weekly"
    anchor + 1.week
  when "monthly"
    anchor.advance(months: 1)
  when "quarterly"
    anchor.advance(months: 3)
  when "bi-yearly"
    anchor.advance(months: 6)
  when "yearly"
    anchor.advance(years: 1)
  end
end

#pending?Boolean

Returns:

  • (Boolean)


38
39
40
# File 'app/models/mensa/export.rb', line 38

def pending?
  status == "pending"
end

#processing?Boolean

Returns:

  • (Boolean)


34
35
36
# File 'app/models/mensa/export.rb', line 34

def processing?
  status == "processing"
end

#repeat_due?(reference_time = Time.current) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
66
67
68
# File 'app/models/mensa/export.rb', line 63

def repeat_due?(reference_time = Time.current)
  return false if repeat.blank? || pending? || processing?

  next_run_at = next_repeat_run_at
  next_run_at.present? && next_run_at <= reference_time
end

#repeat_interval_labelObject



84
85
86
87
88
# File 'app/models/mensa/export.rb', line 84

def repeat_interval_label
  return if repeat.blank?

  I18n.t("mensa.exports.repeat_intervals.#{repeat}", default: repeat)
end

#repeat_labelObject



74
75
76
77
78
79
80
81
82
# File 'app/models/mensa/export.rb', line 74

def repeat_label
  return if repeat.blank?

  I18n.t(
    "mensa.exports.repeats_with_interval",
    default: "Repeats %{interval}",
    interval: repeat_interval_label
  )
end

#repeating?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'app/models/mensa/export.rb', line 70

def repeating?
  repeat.present?
end