diff --git a/.gitignore b/.gitignore index c33bf252..ecdaea19 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ !/tmp/.keep .DS_Store capybara-* +.vscode/ # Ignore Byebug command history file. .byebug_history diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index b238693d..e17dadb4 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -497,3 +497,79 @@ nav.email-templates-menu li a:hover { input.grey { background-color: #c4220d; } + +.grid-wrapper { + display: grid; + grid-template-columns: 200px 200px 200px; + margin-bottom: .5em; +} + +.info-wrapper { + display: flex; + /* justify-content: space-evenly; */ + margin-bottom: 1em; + font-weight: bold; + color: #fff; +} + +.info-wrapper .info { + border-right: 1px solid #ffff; + padding: 15px; + text-align: left; + width: 30em; +} + +.cc-gradient { + background: linear-gradient(to right, #28B4F0, #F0A0C8); +} + +.cc-gradient-reverse { + background: linear-gradient(to right, #F0A0C8, #28B4F0); +} + +.tag { + padding: 0 .7em; + font-size: 12px; + width: fit-content; + color: #fff; + border-radius: 5px; +} + +/* Printer styles */ + +@media print { + header, + input, + footer, + .top-menu { + display: none; + } + + body { + color: #000; + background-color: #fff; + } + + .container { + width: 100%; + } + + table { + break-inside: avoid; + } + + h2 { + page-break-inside: avoid; + margin-top: 30px; + } + + /* Hacky solution to make avoidance of page break work ^^ + https://stackoverflow.com/a/53742871/9328428 + */ + h2::after { + content: ""; + display: block; + height: 200px; + margin-bottom: -200px; + } +} \ No newline at end of file diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 69175778..768876a1 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -3,15 +3,116 @@ class Admin::GroupsController < ApplicationController before_action :require_admin before_action :find_event + GROUP_SIZE = 4 + def index + @event_groups = @event.event_groups + @coaches_count = @event.coach_applications.approved.size + @attendees_count = @event.applications.application_selected.size end + # An action to regenerate + # -> a controller method + # -> button (to call the action) + # -> a route + # -> isolate generate behaviour in its own method + def generate + fill_groups + @event_groups = @event.event_groups redirect_to admin_event_groups_path(@event), notice: "Groups successfully generated" end + def regenerate + @event_groups = @event.event_groups + @event_groups.destroy_all + @event.reload + + fill_groups + redirect_to admin_event_groups_path(@event), notice: "Groups successfully regenerated" + end + private + def fetch_by_key(object, key) + object[key] || [] + end + + def fill_groups + # create a group for each pair of coaches + @coaches = @event.coach_applications.approved.to_a + @coaches.each_slice(1).with_index do |group, index| + event_group = EventGroup.create(event: @event, name: "Group #{index + 1}") + group.each do |coach_application| + event_group.coach_applications << coach_application + end + end + + # get selected attendees from DB + @attendees = @event.applications.application_selected.confirmed.to_a + + grouped_attendees_by_language = @attendees.group_by do |element| + [element.language_de, element.language_en, element.os] + end + + attendees_de_mac = fetch_by_key(grouped_attendees_by_language, [true, false, "mac"]) + attendees_de_windows = fetch_by_key(grouped_attendees_by_language, [true, false, "windows"]) + attendees_de_linux = fetch_by_key(grouped_attendees_by_language, [true, false, "linux"]) + + attendees_en_mac = fetch_by_key(grouped_attendees_by_language, [false, true, "mac"]) + attendees_en_windows = fetch_by_key(grouped_attendees_by_language, [false, true, "windows"]) + attendees_en_linux = fetch_by_key(grouped_attendees_by_language, [false, true, "linux"]) + + attendees_de_en_mac = fetch_by_key(grouped_attendees_by_language, [true, true, "mac"]) + attendees_de_en_windows = fetch_by_key(grouped_attendees_by_language, [true, true, "windows"]) + attendees_de_en_linux = fetch_by_key(grouped_attendees_by_language, [true, true, "linux"]) + + attendees_de_mac_groups = attendees_de_mac.in_groups_of(GROUP_SIZE, false) + attendees_de_windows_groups = attendees_de_windows.in_groups_of(GROUP_SIZE, false) + attendees_de_linux_groups = attendees_de_linux.in_groups_of(GROUP_SIZE, false) + attendees_en_mac_groups = attendees_en_mac.in_groups_of(GROUP_SIZE, false) + attendees_en_windows_groups = attendees_en_windows.in_groups_of(GROUP_SIZE, false) + attendees_en_linux_groups = attendees_en_linux.in_groups_of(GROUP_SIZE, false) + + [attendees_de_mac_groups, attendees_en_mac_groups].each do |groups| + if groups.try :nonzero? + if (groups.last.size < GROUP_SIZE) + groups.last.concat(attendees_de_en_mac.pop(GROUP_SIZE - groups.last.size)) + end + end + end + + [attendees_de_linux_groups, attendees_en_linux_groups].each do |groups| + if groups.try :nonzero? + if(groups.last.size < GROUP_SIZE) + groups.last.concat(attendees_de_en_linux.pop(GROUP_SIZE - groups.last.size)) + end + end + end + + [attendees_de_windows_groups, attendees_en_windows_groups].each do |groups| + if groups.try :nonzero? + if(groups.last.size < GROUP_SIZE) + groups.last.concat(attendees_de_en_windows.pop(GROUP_SIZE - groups.last.size)) + end + end + end + + de_en_groups_mac = attendees_de_en_mac.in_groups_of(GROUP_SIZE, false) + de_en_groups_windows = attendees_de_en_windows.in_groups_of(GROUP_SIZE, false) + de_en_groups_linux = attendees_de_en_windows.in_groups_of(GROUP_SIZE, false) + + all_groups = attendees_de_mac_groups + attendees_de_windows_groups + attendees_de_linux_groups + attendees_en_mac_groups + attendees_en_windows_groups + attendees_en_linux_groups + de_en_groups_mac + de_en_groups_windows + de_en_groups_linux + + + # FIXME: This can cause attendees to not be assigned to event groups + @event.event_groups.each do |event_group| + group_to_add = all_groups.pop(1) + break if group_to_add.nil? + event_group.applications << group_to_add + end + end + def find_event @event = Event.find(params[:event_id]) end diff --git a/app/models/application.rb b/app/models/application.rb index 43c2aefc..d05ad55c 100644 --- a/app/models/application.rb +++ b/app/models/application.rb @@ -21,6 +21,7 @@ class Application < ApplicationRecord scope :cancelled, -> { where(state: :cancelled) } scope :not_marked_as_selected, -> { where(selected_on: nil) } scope :confirmed, -> { where(attendance_confirmed: true) } + scope :language_de, -> { where(language_de: true) } enum state: { rejected: 0, waiting_list: 1, application_selected: 2, cancelled: 3 } diff --git a/app/models/coach.rb b/app/models/coach.rb index ff6ce02d..cd7cf2c3 100644 --- a/app/models/coach.rb +++ b/app/models/coach.rb @@ -3,4 +3,5 @@ class Coach < ApplicationRecord belongs_to :user accepts_nested_attributes_for :user has_many :coach_applications + has_and_belongs_to_many :event_groups end diff --git a/app/models/coach_application.rb b/app/models/coach_application.rb index e6c195ce..01cbf0cb 100644 --- a/app/models/coach_application.rb +++ b/app/models/coach_application.rb @@ -5,6 +5,7 @@ class CoachApplication < ApplicationRecord accepts_nested_attributes_for :event scope :to_contact, -> { where(contacted_at: nil, state: :approved) } + scope :approved, ->{ where(state: :approved) } enum state: { pending: 0, approved: 1, rejected: 2, cancelled: 3 } diff --git a/app/models/event.rb b/app/models/event.rb index 0fc10e9f..4f05e0ef 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,6 +1,7 @@ class Event < ApplicationRecord has_many :applications has_many :coach_applications + has_many :event_groups before_create :copy_templates validates :name, :place, :scheduled_at, :application_start, :application_end, :confirmation_date, :start_time, :end_time, presence: true @@ -26,9 +27,9 @@ def name_and_date end def has_groups? - false + !event_groups.empty? end - + private def right_order_of_dates diff --git a/app/models/event_group.rb b/app/models/event_group.rb new file mode 100644 index 00000000..f1ab6678 --- /dev/null +++ b/app/models/event_group.rb @@ -0,0 +1,5 @@ +class EventGroup < ApplicationRecord + belongs_to :event + has_and_belongs_to_many :coach_applications, join_table: "event_groups_coach_applications" + has_and_belongs_to_many :applications, join_table: "event_groups_applications" +end diff --git a/app/views/admin/groups/index.html.erb b/app/views/admin/groups/index.html.erb index b057b590..3ff358c1 100644 --- a/app/views/admin/groups/index.html.erb +++ b/app/views/admin/groups/index.html.erb @@ -1,5 +1,71 @@ <% if @event.has_groups? %> -has Groups + +
+
Attendees: <%= @attendees_count %>
+
Coaches: <%= @coaches_count %>
+
+ <%= button_to "Regenerate groups", regenerate_admin_event_groups_path(@event), method: :post %> + <% @event_groups.each do |event_group| %> +

<%= event_group.name %>

+ + + + + + + + + + + + + +
AttendeesCoaches
+ <% event_group.applications.map do |application| %> +
+ + <%= application.name %> + + + <% if application.language_de === true && application.language_en === true %> + both languages + <% elsif application.language_de === true && application.language_en === false %> + German + <% else %> + English + <% end %> + + + <% if application.os === "mac" %> + Mac + <% elsif application.os === "windows" %> + Windows + <% elsif application.os === "linux" %> + Linux + <% end %> + +
+ <% end %> +
+ <% event_group.coach_applications.map do |application| %> +
+ + <%= application.coach.name %> + + + <% if application.coach.language_de === true && application.coach.language_en === true %> + both languages + <% elsif application.coach.language_de === true && application.coach.language_en === false %> + German + <% else %> + English + <% end %> + +
+ <% end %> +
+ <%end %> + <% else %> <%= button_to "Generate groups", generate_admin_event_groups_path(@event), method: :post %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 084e2cd4..6ea8a91e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -49,6 +49,7 @@ resources :groups, only: [:index] do collection do post :generate + post :regenerate end end put :complete diff --git a/db/migrate/20200423182041_change_event_group_coaches_to_event_groups_coaches.rb b/db/migrate/20200423182041_change_event_group_coaches_to_event_groups_coaches.rb new file mode 100644 index 00000000..59e2725b --- /dev/null +++ b/db/migrate/20200423182041_change_event_group_coaches_to_event_groups_coaches.rb @@ -0,0 +1,5 @@ +class ChangeEventGroupCoachesToEventGroupsCoaches < ActiveRecord::Migration[5.2] + def change + rename_table :event_group_coaches, :event_groups_coaches + end +end diff --git a/db/migrate/20200423182618_change_event_group_attendees_to_event_groups_applicants.rb b/db/migrate/20200423182618_change_event_group_attendees_to_event_groups_applicants.rb new file mode 100644 index 00000000..d5405e67 --- /dev/null +++ b/db/migrate/20200423182618_change_event_group_attendees_to_event_groups_applicants.rb @@ -0,0 +1,5 @@ +class ChangeEventGroupAttendeesToEventGroupsApplicants < ActiveRecord::Migration[5.2] + def change + rename_table :event_group_attendees, :event_groups_applications + end +end diff --git a/db/migrate/20200429185626_change_event_groups_coaches_to_event_groups_coach_applications.rb b/db/migrate/20200429185626_change_event_groups_coaches_to_event_groups_coach_applications.rb new file mode 100644 index 00000000..630ebe48 --- /dev/null +++ b/db/migrate/20200429185626_change_event_groups_coaches_to_event_groups_coach_applications.rb @@ -0,0 +1,5 @@ +class ChangeEventGroupsCoachesToEventGroupsCoachApplications < ActiveRecord::Migration[5.2] + def change + rename_table :event_groups_coaches, :event_groups_coach_applications + end +end diff --git a/db/schema.rb b/db/schema.rb index ce065c54..4c02f9e6 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.define(version: 2020_02_24_192634) do +ActiveRecord::Schema.define(version: 2020_04_29_185626) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -71,30 +71,30 @@ t.index ["user_id"], name: "index_coaches_on_user_id" end - create_table "event_group_attendees", force: :cascade do |t| - t.bigint "application_id" - t.bigint "event_group_id" + create_table "event_groups", force: :cascade do |t| + t.bigint "event_id" + t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["application_id"], name: "index_event_group_attendees_on_application_id" - t.index ["event_group_id"], name: "index_event_group_attendees_on_event_group_id" + t.index ["event_id"], name: "index_event_groups_on_event_id" end - create_table "event_group_coaches", force: :cascade do |t| - t.bigint "coach_application_id" + create_table "event_groups_applications", force: :cascade do |t| + t.bigint "application_id" t.bigint "event_group_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["coach_application_id"], name: "index_event_group_coaches_on_coach_application_id" - t.index ["event_group_id"], name: "index_event_group_coaches_on_event_group_id" + t.index ["application_id"], name: "index_event_groups_applications_on_application_id" + t.index ["event_group_id"], name: "index_event_groups_applications_on_event_group_id" end - create_table "event_groups", force: :cascade do |t| - t.bigint "event_id" - t.string "name" + create_table "event_groups_coach_applications", force: :cascade do |t| + t.bigint "coach_application_id" + t.bigint "event_group_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["event_id"], name: "index_event_groups_on_event_id" + t.index ["coach_application_id"], name: "index_event_groups_coach_applications_on_coach_application_id" + t.index ["event_group_id"], name: "index_event_groups_coach_applications_on_event_group_id" end create_table "events", id: :serial, force: :cascade do |t| @@ -149,9 +149,9 @@ add_foreign_key "coach_applications", "coaches" add_foreign_key "coach_applications", "events" - add_foreign_key "event_group_attendees", "applications" - add_foreign_key "event_group_attendees", "event_groups" - add_foreign_key "event_group_coaches", "coach_applications" - add_foreign_key "event_group_coaches", "event_groups" add_foreign_key "event_groups", "events" + add_foreign_key "event_groups_applications", "applications" + add_foreign_key "event_groups_applications", "event_groups" + add_foreign_key "event_groups_coach_applications", "coach_applications" + add_foreign_key "event_groups_coach_applications", "event_groups" end