Separa heatmap mobile/desktop, opt-out analytics per operatori, dashboard costi con KPI e trend mensili. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.6 KiB
Ruby
133 lines
4.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Dati simulati per la pagina Admin → Costi (trend 12 mesi, società, revenue).
|
|
# Uso: bundle exec rails runner db/seeds/cost_analytics_demo.rb
|
|
|
|
load Rails.root.join("db/seeds/plans.rb")
|
|
|
|
coach = User.find_or_create_by!(email: "coach@matchlivetv.test") do |u|
|
|
u.name = "Coach Demo"
|
|
u.password = "Password123"
|
|
u.role = "coach"
|
|
end
|
|
|
|
clubs_data = [
|
|
{ name: "Tigers Volley", sport: "volleyball", plan: "premium_full", sessions: 14, hours: 18.5, revenue_yearly: 19900 },
|
|
{ name: "ASD Eagles Milano", sport: "volleyball", plan: "premium_light", sessions: 9, hours: 11.0, revenue_yearly: 5900 },
|
|
{ name: "Volley Stars Roma", sport: "volleyball", plan: "premium_full", sessions: 6, hours: 8.0, revenue_yearly: 19900 },
|
|
{ name: "Basket Juventus U18", sport: "basketball", plan: "premium_light", sessions: 4, hours: 5.5, revenue_yearly: 5900 },
|
|
{ name: "Padova Beach", sport: "volleyball", plan: "free", sessions: 2, hours: 2.0, revenue_yearly: 0 }
|
|
]
|
|
|
|
sport_keys = {
|
|
"volleyball" => "pallavolo",
|
|
"basketball" => "basket"
|
|
}
|
|
|
|
clubs = clubs_data.map do |row|
|
|
club = Club.find_or_create_by!(name: row[:name]) { |c| c.sport = row[:sport] }
|
|
Billing::AssignPlan.call(club: club, plan_slug: row[:plan])
|
|
ClubMembership.find_or_create_by!(user: coach, club: club) { |m| m.role = "owner" }
|
|
team = club.teams.find_or_create_by!(name: "Prima squadra") do |t|
|
|
t.sport_key = sport_keys.fetch(row[:sport], "pallavolo")
|
|
end
|
|
{ club: club, team: team, **row }
|
|
end
|
|
|
|
month_costs = [
|
|
{ offset: 11, hetzner: 89.00, stream: 12.50, s3: 8.20 },
|
|
{ offset: 10, hetzner: 89.00, stream: 18.00, s3: 9.10 },
|
|
{ offset: 9, hetzner: 89.00, stream: 24.50, s3: 10.40 },
|
|
{ offset: 8, hetzner: 89.00, stream: 31.00, s3: 11.80 },
|
|
{ offset: 7, hetzner: 89.00, stream: 22.00, s3: 12.20 },
|
|
{ offset: 6, hetzner: 89.00, stream: 15.50, s3: 13.50 },
|
|
{ offset: 5, hetzner: 89.00, stream: 28.00, s3: 14.80 },
|
|
{ offset: 4, hetzner: 89.00, stream: 35.50, s3: 15.60 },
|
|
{ offset: 3, hetzner: 89.00, stream: 42.00, s3: 16.90 },
|
|
{ offset: 2, hetzner: 89.00, stream: 38.00, s3: 18.20 },
|
|
{ offset: 1, hetzner: 89.00, stream: 45.00, s3: 19.50 },
|
|
{ offset: 0, hetzner: 89.00, stream: 52.00, s3: 21.00 }
|
|
]
|
|
|
|
PlatformCostEntry.delete_all
|
|
|
|
month_costs.each do |row|
|
|
month = Time.zone.today.beginning_of_month - row[:offset].months
|
|
[
|
|
{ label: "Hetzner home + backup", amount: row[:hetzner] },
|
|
{ label: "Nodi stream CPX (overflow)", amount: row[:stream] },
|
|
{ label: "Garage/S3 replay storage", amount: row[:s3] }
|
|
].each do |entry|
|
|
PlatformCostEntry.create!(
|
|
month: month,
|
|
label: entry[:label],
|
|
amount_cents: (entry[:amount] * 100).round,
|
|
notes: "Dato demo seed"
|
|
)
|
|
end
|
|
end
|
|
|
|
# Sessioni e pagamenti nel mese corrente (distribuiti tra società)
|
|
current_month = Time.zone.today.beginning_of_month
|
|
clubs.each_with_index do |row, index|
|
|
club = row[:club]
|
|
team = row[:team]
|
|
sessions_count = row[:sessions]
|
|
total_secs = (row[:hours] * 3600).round
|
|
|
|
sessions_count.times do |n|
|
|
secs = (total_secs / sessions_count.to_f).round
|
|
day = current_month + (n % 27).days
|
|
started = day.change(hour: 10 + (n % 6), min: 0)
|
|
ended = started + secs.seconds
|
|
match = team.matches.create!(
|
|
opponent_name: "Avversario demo #{n + 1}",
|
|
sport_key: team.sport_key,
|
|
scheduled_at: started
|
|
)
|
|
StreamSession.create!(
|
|
match: match,
|
|
user: coach,
|
|
platform: "matchlivetv",
|
|
status: "ended",
|
|
started_at: started,
|
|
ended_at: ended,
|
|
total_duration_secs: secs
|
|
)
|
|
end
|
|
|
|
if row[:revenue_yearly].positive?
|
|
payment = Billing::Payment.find_or_initialize_by(club: club, paid_at: current_month + (index + 3).days)
|
|
payment.assign_attributes(
|
|
status: "paid",
|
|
provider: "stripe",
|
|
amount_cents: row[:revenue_yearly],
|
|
currency: "EUR",
|
|
plan_slug: row[:plan]
|
|
)
|
|
payment.save!
|
|
end
|
|
|
|
# Storage simulato (cumulativo)
|
|
sample_session = team.matches.last&.stream_sessions&.first
|
|
next unless sample_session
|
|
|
|
rec = Recording.find_or_initialize_by(stream_session: sample_session)
|
|
rec.assign_attributes(
|
|
team: team,
|
|
status: "ready",
|
|
privacy_status: "unlisted",
|
|
storage_backend: "s3",
|
|
storage_policy: "retained",
|
|
byte_size: (500_000_000 + index * 180_000_000),
|
|
duration_secs: 3600
|
|
)
|
|
rec.save!
|
|
end
|
|
|
|
puts "Cost analytics demo OK:"
|
|
puts " Voci costo: #{PlatformCostEntry.count} (12 mesi)"
|
|
puts " Sessioni demo mese corrente: #{StreamSession.where(status: 'ended', ended_at: current_month..current_month.end_of_month).count}"
|
|
puts " Admin: http://localhost:3000/admin/costs"
|
|
puts " Login admin: admin / AdminPass123"
|