From 4bbe59a99657e698c9afbbf30c85d6e17eaf7645 Mon Sep 17 00:00:00 2001
From: Teal <git@teal.is>
Date: Sun, 26 May 2024 23:46:00 +0200
Subject: [PATCH] tons more stuff

---
 .../stylesheets/application.tailwind.css      |  4 +-
 app/controllers/conferences_controller.rb     |  2 +-
 app/controllers/speakers_controller.rb        |  5 +++
 app/controllers/users_controller.rb           |  8 ++--
 app/helpers/speakers_helper.rb                |  2 +
 .../republica_2023_or_later/import_job.rb     | 43 ++++++++++---------
 .../telegram_group_chat_notification_job.rb   |  2 +
 app/models/conference.rb                      |  2 +
 app/models/session.rb                         |  3 ++
 app/models/session_speaker.rb                 |  4 ++
 app/models/speaker.rb                         |  5 +++
 app/models/user.rb                            |  4 ++
 app/views/assignments/index.html.erb          |  2 +-
 app/views/conferences/show.html.erb           | 24 +++++++++--
 app/views/layouts/application.html.erb        | 23 +++++-----
 app/views/sessions/_session.html.erb          | 26 ++++++++---
 app/views/speakers/show.html.erb              |  6 +++
 app/views/users/profile.html.erb              |  6 +++
 config/initializers/subscribers.rb            |  4 +-
 config/routes.rb                              |  2 +
 ...40526164422_add_password_digest_to_user.rb |  5 +++
 .../20240526165259_create_session_speakers.rb | 10 +++++
 db/schema.rb                                  | 14 +++++-
 db/seeds.rb                                   |  8 ++--
 test/controllers/speakers_controller_test.rb  |  8 ++++
 test/fixtures/session_speakers.yml            |  9 ++++
 test/models/session_speaker_test.rb           |  7 +++
 27 files changed, 183 insertions(+), 55 deletions(-)
 create mode 100644 app/controllers/speakers_controller.rb
 create mode 100644 app/helpers/speakers_helper.rb
 create mode 100644 app/models/session_speaker.rb
 create mode 100644 app/views/speakers/show.html.erb
 create mode 100644 db/migrate/20240526164422_add_password_digest_to_user.rb
 create mode 100644 db/migrate/20240526165259_create_session_speakers.rb
 create mode 100644 test/controllers/speakers_controller_test.rb
 create mode 100644 test/fixtures/session_speakers.yml
 create mode 100644 test/models/session_speaker_test.rb

diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css
index e3e1d6d..cca4f71 100644
--- a/app/assets/stylesheets/application.tailwind.css
+++ b/app/assets/stylesheets/application.tailwind.css
@@ -26,7 +26,7 @@ select {
 }
 .session-holder { @apply w-full; }
 .session {
-  @apply border p-2 bg-slate-100 border-slate-300 rounded-md w-full flex flex-col hover:shadow-2xl;
+  @apply border p-2 bg-slate-100 border-slate-300 rounded-md w-full hover:shadow-2xl;
 
   &.backup-needed {
     @apply bg-amber-100 border-amber-400;
@@ -58,6 +58,6 @@ select {
 }
 .main-nav {
   a {
-    @apply underline hover:text-blue-600 hover:border-b hover:border-blue-600;
+    @apply underline hover:text-blue-600 hover:border-blue-600;
   }
 }
\ No newline at end of file
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index 2e27113..b44d4e0 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -5,7 +5,7 @@ class ConferencesController < ApplicationController
 
   def show
     @conference = Conference.find_by(slug: params[:slug])
-    @sessions = @conference.sessions.where.not(starts_at: nil).includes(:stage, :assignments).where(stage: { name: ["Stage 1", "Stage 2"] }).order(:starts_at)
+    @sessions = @conference.sessions.where.not(starts_at: nil).includes(:stage, :assignments).where(stage: { name: ["Stage 1", "Stage 2", "Standby"] }).order(:starts_at)
     if params[:date]
       date = Time.parse(params[:date])
       logger.debug(date)
diff --git a/app/controllers/speakers_controller.rb b/app/controllers/speakers_controller.rb
new file mode 100644
index 0000000..3b56f2b
--- /dev/null
+++ b/app/controllers/speakers_controller.rb
@@ -0,0 +1,5 @@
+class SpeakersController < ApplicationController
+  def show
+    @speaker = Speaker.find_by(ref_id: params[:ref_id])
+  end
+end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 05b666e..8c4133c 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -21,21 +21,23 @@ class UsersController < ApplicationController
 
   def update_profile
     @user = current_user
+    logger.debug(@user)
     if @user.update(user_params)
+      logger.debug(@user)
       flash.now[:notice] = "Profile updated successfully!"
       respond_to do |format|
         format.html { redirect_to profile_path, notice: "Profile updated successfully!"  }
-        #format.turbo_stream { render turbo_stream: turbo_stream.replace("flash", partial: "shared/flash") }
+        format.turbo_stream { render turbo_stream: turbo_stream.replace("flash", partial: "shared/flash") }
       end
     else
-      render :edit
+      render :profile
     end
   end
 
   private
 
   def user_params
-    params.require(:user).permit(:name, :email, :avatar_color, :telegram_username)
+    params.require(:user).permit(:name, :email, :password, :avatar_color, :telegram_username)
   end
 
   def require_login
diff --git a/app/helpers/speakers_helper.rb b/app/helpers/speakers_helper.rb
new file mode 100644
index 0000000..b506a69
--- /dev/null
+++ b/app/helpers/speakers_helper.rb
@@ -0,0 +1,2 @@
+module SpeakersHelper
+end
diff --git a/app/jobs/republica_2023_or_later/import_job.rb b/app/jobs/republica_2023_or_later/import_job.rb
index f0c40f0..b219f8e 100644
--- a/app/jobs/republica_2023_or_later/import_job.rb
+++ b/app/jobs/republica_2023_or_later/import_job.rb
@@ -3,6 +3,23 @@ require 'httparty'
 module Republica2023OrLater
   class ImportJob < ApplicationJob
     queue_as :default
+    
+    def import_speakers(conference, url)
+      response = HTTParty.get(url)
+      if response.success?
+        speakers = JSON.parse(response.body)
+        speakers.each do |speaker_data|
+          Speaker.find_or_initialize_by(ref_id: speaker_data['uid'], conference:).tap do |speaker|
+            speaker.name = speaker_data['name_raw']
+            speaker.position = speaker_data['position']
+            speaker.description = speaker_data['bio']
+            speaker.save!
+          end
+        end
+      else
+        Rails.logger.error "Failed to fetch speakers from #{url}"
+      end
+    end
 
     def import_sessions(conference, url)
       response = HTTParty.get(url)
@@ -31,30 +48,14 @@ module Republica2023OrLater
             session.starts_at = session_data['datetime_start']
             session.ends_at = session_data['datetime_end']
             session.url = "https://re-publica.com#{session_data['path']}"
+            session.speakers = session_data['speaker_uid'].map { |speaker_uid| conference.speakers.find_by!(ref_id: speaker_uid) }
             session.save!
-          end
-        end
+          end  
+        end  
       else
         Rails.logger.error "Failed to fetch sessions from #{url}"
-      end
-    end
-
-    def import_speakers(conference, url)
-      response = HTTParty.get(url)
-      if response.success?
-        speakers = JSON.parse(response.body)
-        speakers.each do |speaker_data|
-          Speaker.find_or_initialize_by(ref_id: speaker_data['uid'], conference:).tap do |speaker|
-            speaker.name = speaker_data['name']
-            speaker.position = speaker_data['position']
-            speaker.description = speaker_data['bio']
-            speaker.save!
-          end
-        end
-      else
-        Rails.logger.error "Failed to fetch speakers from #{url}"
-      end
-    end
+      end  
+    end  
 
     def perform(conference_slug, *args)
       conference = Conference.find_by(slug: conference_slug)
diff --git a/app/jobs/telegram_group_chat_notification_job.rb b/app/jobs/telegram_group_chat_notification_job.rb
index ea10213..b8bb906 100644
--- a/app/jobs/telegram_group_chat_notification_job.rb
+++ b/app/jobs/telegram_group_chat_notification_job.rb
@@ -5,6 +5,8 @@ class TelegramGroupChatNotificationJob < NotificationJob
 
   def perform(**args)
     channel = NotificationChannel.find_by(name: 'telegram_group_chat')
+    return unless channel.data
+    return
     token = channel.data['token']
     Telegram::Bot::Client.run(token) do |bot|
       bot.api.send_message(chat_id: args[:target], text: args[:text])
diff --git a/app/models/conference.rb b/app/models/conference.rb
index a4efb3d..6a6d08e 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -1,6 +1,8 @@
 class Conference < ApplicationRecord
   has_many :sessions
+  has_many :speakers
   has_many :stages
+  has_many :revision_sets
 
   serialize :data, coder: JSON
 
diff --git a/app/models/session.rb b/app/models/session.rb
index 1766530..46180ee 100644
--- a/app/models/session.rb
+++ b/app/models/session.rb
@@ -3,6 +3,9 @@ class Session < ApplicationRecord
   belongs_to :stage
   has_many :assignments
   has_many :users, through: :assignments
+  has_many :session_speakers, dependent: :destroy
+  has_many :speakers, through: :session_speakers
+
 
   scope :scheduled, -> { where(status: 'scheduled') }
 
diff --git a/app/models/session_speaker.rb b/app/models/session_speaker.rb
new file mode 100644
index 0000000..b4bb73f
--- /dev/null
+++ b/app/models/session_speaker.rb
@@ -0,0 +1,4 @@
+class SessionSpeaker < ApplicationRecord
+  belongs_to :session
+  belongs_to :speaker
+end
diff --git a/app/models/speaker.rb b/app/models/speaker.rb
index 5f40005..de7b9b8 100644
--- a/app/models/speaker.rb
+++ b/app/models/speaker.rb
@@ -1,10 +1,15 @@
 class Speaker < ApplicationRecord
   belongs_to :conference
+  has_many :session_speakers, dependent: :destroy
+  has_many :sessions, through: :session_speakers
 
   validates :ref_id, uniqueness: { scope: :conference_id }
 
   after_update :notify_if_changed
 
+  def to_param
+    ref_id
+  end
 
   private
 
diff --git a/app/models/user.rb b/app/models/user.rb
index f343dde..8ca777b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -8,6 +8,10 @@ class User < ApplicationRecord
 
   after_initialize :set_avatar_color
 
+  def errors
+    super.tap { |errors| errors.delete(:password, :blank) if password_digest.nil? }
+  end
+
   def has_password?
     !password_digest.nil?
   end
diff --git a/app/views/assignments/index.html.erb b/app/views/assignments/index.html.erb
index 9506530..86bfbb2 100644
--- a/app/views/assignments/index.html.erb
+++ b/app/views/assignments/index.html.erb
@@ -3,7 +3,7 @@
     <div class="my-8">
       <h4 class="text-xl my-2"><%= link_to user.name, user_assignments_path(user) %> <span class="font-normal"><%= link_to user_assignments_path(user, format: 'ics') do %><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="ml-2 mb-1 size-4 inline-block stroke-slate-400 fill-slate-400"><path fill-rule="evenodd" d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" clip-rule="evenodd"></path></svg><% end %></span></h4>
         <% assignments.group_by { |a| a.session.starts_at.strftime('%Y-%m-%d') }.each do |date, assignments_on_date| %>
-          <h5><%= date %></h5>
+          <h5 class="text-base mt-2"><%= date %></h5>
           <ol class="list-inside">
             <% assignments_on_date.each do |assignment| %>
               <li>
diff --git a/app/views/conferences/show.html.erb b/app/views/conferences/show.html.erb
index 2c545fa..054bb99 100644
--- a/app/views/conferences/show.html.erb
+++ b/app/views/conferences/show.html.erb
@@ -8,8 +8,20 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone)
 #current_time = @sessions_by_date[@conference.days.first].first.starts_at.advance(minutes: 5)
 %>
 <div>
-  <h1 class="text-2xl font-bold my-6"><%= @conference.name %></h1>
-  <p><small><%= @conference.starts_at_in_local_time.strftime("%b %d, %Y") %> &ndash; <%= @conference.ends_at_in_local_time.strftime("%b %d, %Y") %></small></p>
+  <h1 class="text-2xl font-bold my-2"><%= @conference.name %></h1>
+  <p class="text-xs mb-6">
+    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline-block size-4 stroke-slate-500">
+      <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
+    </svg>
+    <%= @conference.starts_at_in_local_time.strftime("%b %d, %Y") %> &ndash; <%= @conference.ends_at_in_local_time.strftime("%b %d, %Y") %>
+    <% if @conference.location %>
+      <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline-block size-4 stroke-slate-500">
+        <path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
+        <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z" />
+      </svg>
+      <%= @conference.location %>
+    <% end %>
+  </p>
 
   <div class="text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700">
     <ul class="flex flex-wrap -mb-px">
@@ -104,7 +116,13 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone)
             <h4><%= stage.name %></h4>
             <div class="stage-sessions">
               <% sessions.each do |session| %>
-                <div class="session-holder hover:z-30" style="position: absolute; top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px; max-height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour %>px; height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour %>px;">
+                <div class="session-holder hover:z-30 h-full hover:!h-auto" style="
+                  position: absolute;
+                  top: <%= (session.starts_at - timeline_starts_at) / 3600.0 * pixels_per_hour %>px;
+                  max-height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour %>px;
+                  min-height: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour %>px;
+                  hxeight: <%= (session.ends_at - session.starts_at) / 3600.0 * pixels_per_hour %>px;
+                ">
                   <%= render partial: "sessions/session", locals: { session: session } %>
                 </div>
               <% end %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 1d333c1..4b08dc1 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -12,22 +12,23 @@
   </head>
 
   <body>
-    <nav class="main-nav bg-slate-100 border-b border-slate-200">
+    <nav class="main-nav bg-slate-100 border-b border-slate-200 shadow">
       <div class="container mx-auto p-5 flex flex-row justify-between">
         <div class="main-nav-left flex flex-row gap-4 items-center">
-          <h1 class="text-xl font-bold">re:scheduled</h1>
+          <h1 class="text-xl font-bold"><%= link_to 're:scheduled', '/', class: "!no-underline" %></h1>
           <div><%= link_to 'Conferences', conferences_path %></div>
+          <div><%= link_to 'Assignments', assignments_path %></div>
         </div>
         <div class="main-nav-right flex-row">
-        <% if logged_in? %>
-          logged in as <%= render partial: 'application/user_avatar', locals: { user: current_user } %>
-          <%= link_to 'Profile', profile_path %>
-          <%= link_to 'Assignments', user_assignments_path(current_user) %>
-          <%= link_to 'Logout', logout_path, data: { turbo_method: :post } %>
-        <% else %>
-          Not logged in
-          <%= link_to 'Login', login_path %>
-        <% end %>
+          <% if logged_in? %>
+            logged in as <%= render partial: 'application/user_avatar', locals: { user: current_user } %>
+            <%= link_to 'My Profile', profile_path %>
+            <%= link_to 'My Assignments', user_assignments_path(current_user) %>
+            <%= link_to 'Logout', logout_path, data: { turbo_method: :post } %>
+          <% else %>
+            not logged in
+            <%= link_to 'Login', login_path %>
+          <% end %>
         </div>
       </div>
     </nav>
diff --git a/app/views/sessions/_session.html.erb b/app/views/sessions/_session.html.erb
index 4079c1a..2f2fb48 100644
--- a/app/views/sessions/_session.html.erb
+++ b/app/views/sessions/_session.html.erb
@@ -1,17 +1,33 @@
 <% unassigned_users = @users - session.assignments.collect(&:user) %>
 <%= turbo_frame_tag dom_id(session) do %>
-  <div class="session shadow hover:shadow-lg overflow-scroll hover:h-max text-sm w-full h-full <%= session.translators_needed? ? "translators-needed" : "no-translators-needed" %> <%= session.backup_needed? ? "backup-needed" : "no-backup-needed" %> <%= session.has_assignees? ? "has-assignees" : "no-assignees" %>">
+  <div class="session shadow hover:shadow-lg overflow-scroll text-sm w-full !h-full min-h-full hover:!min-h-max <%= session.translators_needed? ? "translators-needed" : "no-translators-needed" %> <%= session.backup_needed? ? "backup-needed" : "no-backup-needed" %> <%= session.has_assignees? ? "has-assignees" : "no-assignees" %>">
     <h4>
       <small class="text-2xs uppercase font-light bg-black/10 rounded-sm p-1 mr-1 lang-<%= session.language %>"><%= session.language %></small>
       <%= link_to session.title, session.url, target: "_top" %>
     </h4>
-    <% if false %>
     <div>
-      <span class="session-time"><%= session.starts_at.strftime('%H:%M') %> - <%= session.ends_at.strftime('%H:%M') %></span>
-       &ndash;
-      <span class="session-location text-sm font-medium"><%= session.stage.name %></span>
+      <span class="session-time text-xs mr-1">
+        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline-block size-4 stroke-slate-500">
+          <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
+        </svg>
+        <%= session.starts_at.strftime('%H:%M') %> - <%= session.ends_at.strftime('%H:%M') %>
+        (<%= ((session.ends_at - session.starts_at) / 60.0).to_i %>min)
+      </span>
+      <span class="session-location text-xs font-medium">
+        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline-block size-4 stroke-slate-500">
+          <path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
+          <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z" />
+        </svg>
+        <%= session.stage.name %>
+      </span>
     </div>
+    <div>
+    <ul class="flex flex-wrap text-xs gap-1">
+    <% session.speakers.each do |speaker| %>
+      <li><%= link_to speaker.name, conference_speaker_path(session.conference, speaker.ref_id), class: "underline" %></li>
     <% end %>
+    </uL>
+    </div>
     
     <% if false and (session.translators_needed? or session.backup_needed?) %>
       <% if session.translators_needed? %>
diff --git a/app/views/speakers/show.html.erb b/app/views/speakers/show.html.erb
new file mode 100644
index 0000000..5780d9e
--- /dev/null
+++ b/app/views/speakers/show.html.erb
@@ -0,0 +1,6 @@
+<div>
+  <h1 class="font-bold text-4xl"><%= @speaker.name %></h1>
+  <h2 class="font-medium text-2xl"><%= @speaker.position %></h2>
+  <p><%= @speaker.description.html_safe %></p>
+  <% # = link_to "#{@speaker.name} at #{@speaker.conference.name}", %>
+</div>
diff --git a/app/views/users/profile.html.erb b/app/views/users/profile.html.erb
index f655398..b79fdb8 100644
--- a/app/views/users/profile.html.erb
+++ b/app/views/users/profile.html.erb
@@ -1,6 +1,7 @@
 <div>
   <h1 class="font-bold text-4xl">Profile</h1>
   <%= form_with(model: @user, url: update_profile_path, local: true) do |form| %>
+
     <div class="field">
       <%= form.label :name %>
       <%= form.text_field :name %>
@@ -11,6 +12,11 @@
       <%= form.text_field :email %>
     </div>
 
+    <div class="field">
+      <%= form.label :password %>
+      <%= form.password_field :password %>
+    </div>
+
     <div class="field">
       <%= form.label :avatar_color %>
       <%= form.color_field :avatar_color %>
diff --git a/config/initializers/subscribers.rb b/config/initializers/subscribers.rb
index ad9d4de..0b97274 100644
--- a/config/initializers/subscribers.rb
+++ b/config/initializers/subscribers.rb
@@ -1,4 +1,4 @@
 Rails.application.config.to_prepare do
-  TelegramBotSubscriber.subscribe
-  AssignmentAuditSubscriber.subscribe
+  # TelegramBotSubscriber.subscribe
+  # AssignmentAuditSubscriber.subscribe
 end
diff --git a/config/routes.rb b/config/routes.rb
index ebfe923..4977ab0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
 Rails.application.routes.draw do
+  get 'speakers/show'
   mount Crono::Engine, at: '/crono'
 
   get 'login', to: 'users#login', as: :login
@@ -22,6 +23,7 @@ Rails.application.routes.draw do
     resources :sessions, param: :ref_id do
       resources :assignments, only: [:create, :destroy]
     end
+    resources :speakers, param: :ref_id
   end
 
   resources :assignments, only: [:index] do
diff --git a/db/migrate/20240526164422_add_password_digest_to_user.rb b/db/migrate/20240526164422_add_password_digest_to_user.rb
new file mode 100644
index 0000000..cad444f
--- /dev/null
+++ b/db/migrate/20240526164422_add_password_digest_to_user.rb
@@ -0,0 +1,5 @@
+class AddPasswordDigestToUser < ActiveRecord::Migration[7.1]
+  def change
+    add_column :users, :password_digest, :string
+  end
+end
diff --git a/db/migrate/20240526165259_create_session_speakers.rb b/db/migrate/20240526165259_create_session_speakers.rb
new file mode 100644
index 0000000..115cb06
--- /dev/null
+++ b/db/migrate/20240526165259_create_session_speakers.rb
@@ -0,0 +1,10 @@
+class CreateSessionSpeakers < ActiveRecord::Migration[7.1]
+  def change
+    create_table :session_speakers do |t|
+      t.belongs_to :session, null: false, foreign_key: true
+      t.belongs_to :speaker, null: false, foreign_key: true
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a593153..ed4b008 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.1].define(version: 2024_05_26_151339) do
+ActiveRecord::Schema[7.1].define(version: 2024_05_26_165259) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
@@ -91,6 +91,15 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_26_151339) do
     t.index ["conference_id"], name: "index_revisions_on_conference_id"
   end
 
+  create_table "session_speakers", force: :cascade do |t|
+    t.integer "session_id", null: false
+    t.integer "speaker_id", null: false
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["session_id"], name: "index_session_speakers_on_session_id"
+    t.index ["speaker_id"], name: "index_session_speakers_on_speaker_id"
+  end
+
   create_table "sessions", force: :cascade do |t|
     t.string "title"
     t.string "language"
@@ -246,12 +255,15 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_26_151339) do
     t.datetime "updated_at", null: false
     t.string "avatar_color"
     t.string "telegram_username"
+    t.string "password_digest"
   end
 
   add_foreign_key "assignments", "sessions"
   add_foreign_key "assignments", "users"
   add_foreign_key "revision_sets", "conferences"
   add_foreign_key "revisions", "conferences"
+  add_foreign_key "session_speakers", "sessions"
+  add_foreign_key "session_speakers", "speakers"
   add_foreign_key "sessions", "conferences"
   add_foreign_key "sessions", "stages"
   add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
diff --git a/db/seeds.rb b/db/seeds.rb
index ebbcef7..f9464ce 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -35,12 +35,10 @@ Conference.find_or_create_by!(slug: "rp2024") do |c|
 end
 
 %w[Teal hdsjulian Sophie bergpiratin sblsg Max aerowaffle ningwie Senana ToniHDS].each do |username|
-  User.find_or_create_by!(name: username, email: "c3lingo+#{username}@x.moeffju.net")
+  User.find_or_create_by(name: username) do |u|
+    u.email = "c3lingo+#{username}@x.moeffju.net"
+  end
 end
-u = User.find_by(name: 'sblsg')
-u.name = 'sebalis'
-u.telegram_username = 'sblsg'
-u.save!
 
 NotificationChannel.create!(
   name: 'telegram_group_chat',
diff --git a/test/controllers/speakers_controller_test.rb b/test/controllers/speakers_controller_test.rb
new file mode 100644
index 0000000..7052f11
--- /dev/null
+++ b/test/controllers/speakers_controller_test.rb
@@ -0,0 +1,8 @@
+require "test_helper"
+
+class SpeakersControllerTest < ActionDispatch::IntegrationTest
+  test "should get show" do
+    get speakers_show_url
+    assert_response :success
+  end
+end
diff --git a/test/fixtures/session_speakers.yml b/test/fixtures/session_speakers.yml
new file mode 100644
index 0000000..51a1d98
--- /dev/null
+++ b/test/fixtures/session_speakers.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+  session: one
+  speaker: one
+
+two:
+  session: two
+  speaker: two
diff --git a/test/models/session_speaker_test.rb b/test/models/session_speaker_test.rb
new file mode 100644
index 0000000..e6e0a2d
--- /dev/null
+++ b/test/models/session_speaker_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class SessionSpeakerTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end
-- 
GitLab