From 4d55119c32bd6fac284e9830f923fb032ffd39f4 Mon Sep 17 00:00:00 2001
From: Felix Eckhofer <felix@eckhofer.com>
Date: Fri, 27 Dec 2024 17:54:01 +0100
Subject: [PATCH] Add leaderboard

---
 app/controllers/users_controller.rb  |  3 +++
 app/models/session.rb                |  4 ++++
 app/models/user.rb                   | 21 +++++++++++++++++++++
 app/views/users/leaderboard.html.erb | 22 ++++++++++++++++++++++
 config/routes.rb                     |  1 +
 5 files changed, 51 insertions(+)
 create mode 100644 app/views/users/leaderboard.html.erb

diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 3e74dea..2a1503d 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,2 +1,5 @@
 class UsersController < ApplicationController
+  def leaderboard
+    @workload_data = User.leaderboard
+  end
 end
diff --git a/app/models/session.rb b/app/models/session.rb
index bfc2247..3f4ff84 100644
--- a/app/models/session.rb
+++ b/app/models/session.rb
@@ -56,6 +56,10 @@ class Session < ApplicationRecord
     return filedrop_files.exists? || filedrop_comments.exists?
   end
 
+  def duration_minutes
+    return (ends_at - starts_at) / 60.0
+  end
+
   private
 
   def notify_if_changed
diff --git a/app/models/user.rb b/app/models/user.rb
index aa06e39..1652be2 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -30,6 +30,23 @@ class User < ApplicationRecord
     end
   end
 
+  def self.leaderboard
+    all.map { |u| [u.name, u.workload_minutes] }
+      .sort_by { |_, workload| -workload }
+      .reject { |_, workload| workload.zero? }
+      .to_h
+  end
+
+  class Session < ApplicationRecord
+    has_many :assignments
+
+    def workload_minutes
+      # Logic to calculate workload in minutes
+      60 # This is just a placeholder
+    end
+  end
+
+
   def errors
     super.tap { |errors| errors.delete(:password, :blank) if password.nil? }
   end
@@ -66,6 +83,10 @@ class User < ApplicationRecord
     self.avatar_color = "##{r.to_s(16).rjust(2, '0')}#{g.to_s(16).rjust(2, '0')}#{b.to_s(16).rjust(2, '0')}"
   end
 
+  def workload_minutes
+    Assignment.includes(:session).where(user: self).sum { | a | a.session.duration_minutes }
+  end
+
   private
 
   def valid_invitation_token
diff --git a/app/views/users/leaderboard.html.erb b/app/views/users/leaderboard.html.erb
new file mode 100644
index 0000000..fb22a0c
--- /dev/null
+++ b/app/views/users/leaderboard.html.erb
@@ -0,0 +1,22 @@
+<div class="w-full">
+  <h1 class="text-xl my-4">Leaderboard</h1>
+    <dl class="w-full max-w-4xl mx-auto">
+      <% top_workload = @workload_data.values.max %>
+      <% @workload_data.each_with_index do |(username, workload), index| %>
+      <% hours, minutes = workload.divmod(60) %>
+        <div class="bg-gray-50 px-4 py-2 sm:flex sm:items-center sm:gap-4">
+          <dt class="text-sm font-medium text-gray-500 flex-none w-1/4 sm:w-1/6"><%= username %><%= " 👑" if index == 0 %></dt>
+          <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:flex-1">
+            <div class="relative pt-1">
+              <div class="overflow-hidden h-2 mb-1 text-xs flex rounded bg-green-200">
+                <div style="width:<%= (100 * workload / top_workload).round %>%;" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-500"></div>
+              </div>
+              <span class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded text-green-600 bg-green-200 last:mr-0 mr-1">
+                <%= hours > 0 ? "#{hours}h #{minutes.round}m" : "#{minutes.round} minutes" %>
+              </span>
+            </div>
+          </dd>
+        </div>
+      <% end %>
+    </dl>
+</div>
diff --git a/config/routes.rb b/config/routes.rb
index b66d9e1..a2c4b39 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -4,6 +4,7 @@ Rails.application.routes.draw do
   mount ActionCable.server => '/cable'
 
   get 'speakers/show'
+  get 'users/leaderboard'
 
   # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
   # Can be used by load balancers and uptime monitors to verify that the app is live.
-- 
GitLab