diff --git a/Gemfile b/Gemfile index 3abbce4..cdea679 100644 --- a/Gemfile +++ b/Gemfile @@ -42,16 +42,13 @@ gem 'geocoder' gem 'paper_trail', '~> 3.0.5' gem 'sitemap_generator' gem 'activerecord-session_store' -# gem 'paypal-express', '0.7.1' gem 'sass-json-vars' gem 'premailer-rails' gem 'redcarpet' gem 'sidekiq' gem 'letter_opener' gem 'launchy' -# gem 'axlsx' -# gem 'excelinator' -gem 'to_spreadsheet'#, :git => 'git://github.com/glebm/to_spreadsheet.git' +gem 'to_spreadsheet', :git => 'git://github.com/glebm/to_spreadsheet.git' group :test do gem 'rspec' diff --git a/app/assets/javascripts/housing.js b/app/assets/javascripts/housing.js index 34c2f05..87c644b 100644 --- a/app/assets/javascripts/housing.js +++ b/app/assets/javascripts/housing.js @@ -92,84 +92,4 @@ closeGuestSelector(); } }); - function closeOnTop() { - document.documentElement.removeAttribute('data-ontop'); - document.getElementById('guest_id').value = ''; - var target = document.querySelector('.on-top-target'); - target.removeAttribute('style'); - document.querySelector('body').removeAttribute('style'); - forEachElement('.on-top-control', function(control) { - control.classList.remove('on-top-control'); - }); - } - forEachElement('#guests .guest', function(guest) { - var button = guest.querySelector('.set-host'); - button.addEventListener('click', function(event) { - var target = document.querySelector('.on-top-target'); - var body = document.querySelector('body'); - // maintain our current height - body.setAttribute('style', 'height: ' + body.clientHeight + 'px'); - document.documentElement.setAttribute('data-ontop', 'set-host'); - guest.classList.add('on-top-control'); - target.setAttribute('style', 'bottom: ' + guest.clientHeight + 'px'); - document.getElementById('guest_id').value = guest.dataset.id; - }); - }); - forEachElement('#hosts .host', function(host) { - initHost(host); - }); - - function initHost(host) { - forEachElement('.place-guest', function(button) { - button.addEventListener('click', function(event) { - var guest_id = document.getElementById('guest_id').value; - if (guest_id) { - var guest = document.getElementById('guest-' + guest_id); - var form = document.getElementById('hosts'); - var data = new FormData(form); - - host.classList.add('requesting'); - if (guest.dataset.affectedHosts) { - data.append('affected-hosts', guest.dataset.affectedHosts); - forEach(guest.dataset.affectedHosts.split(','), function(host_id) { - h = document.getElementById('host-' + host_id); - if (h) { - h.classList.add('requesting'); - } - }); - } - data.append('button', button.value); - - var request = new XMLHttpRequest(); - request.onreadystatechange = function() { - if (request.readyState == 4) { - if (request.status == 200) { - var response = JSON.parse(request.responseText); - for (var host_id in response.hosts) { - host_element = document.getElementById('host-' + host_id); - widget = response.hosts[host_id]; - host_element.className = widget.class; - host_element.querySelector('.guests').innerHTML = widget.html; - initHost(host_element); - host_element.classList.remove('requesting'); - } - for (var guest_id in response.affected_hosts) { - guest_element = document.getElementById('guest-' + guest_id); - if (guest_element) { - guest_element.setAttribute('data-affected-hosts', response.affected_hosts[guest_id].join(',')); - } - } - } - } - } - request.open('POST', form.getAttribute('action'), true); - request.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - request.send(data); - } - }); - }, host); - } - forEachElement('.on-top-close', function(button) { - button.addEventListener('click', closeOnTop); - }); })(); diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index 4dadb48..2565ce0 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -288,7 +288,7 @@ class ConferencesController < ApplicationController @organizations = Organization.all if request.format.xlsx? - logger.info "Generating stats.xls" + logger.info "Generating organizations.xls" @excel_data = { columns: [:name, :street_address, :city, :subregion, :country, :postal_code, :email, :phone, :status], keys: { @@ -439,6 +439,80 @@ class ConferencesController < ApplicationController when :housing # do a full analysis analyze_housing + + if request.format.xlsx? + logger.info "Generating housing.xls" + @excel_data = { + columns: [:name, :phone, :street_address, :email, :availability, :considerations, :empty, :empty, :empty, :guests], + keys: { + name: 'forms.labels.generic.name', + street_address: 'forms.labels.generic.street_address', + email: 'forms.labels.generic.email', + phone: 'forms.labels.generic.phone', + availability: 'articles.conference_registration.headings.host.availability', + considerations: 'articles.conference_registration.headings.host.considerations' + }, + column_types: { + name: :bold, + guests: :table + }, + data: [], + } + @hosts.each do | id, host | + data = (host.housing_data || {}) + host_data = { + name: host.user.name, + street_address: data['address'], + email: host.user.email, + phone: data['phone'], + availability: data['availability'].present? && data['availability'][1].present? ? view_context.date_span(data['availability'][0].to_date, data['availability'][1].to_date) : '', + considerations: (data['considerations'].map { | consideration | view_context._"articles.conference_registration.host.considerations.#{consideration}" }).join(', '), + empty: '', + guests: { + columns: [:name, :area, :email, :arrival_departure, :allergies, :food, :companion, :city], + keys: { + name: 'forms.labels.generic.name', + area: 'articles.workshops.headings.space', + email: 'forms.labels.generic.email', + arrival_departure: 'articles.admin.housing.headings.arrival_departure', + companion: 'forms.labels.generic.companion', + city: 'forms.labels.generic.city', + food: 'forms.labels.generic.food', + allergies: 'forms.labels.generic.allergies' + }, + column_types: { + name: :bold + }, + data: [] + } + } + + @housing_data[id][:guests].each do | space, space_data | + space_data.each do | guest_id, guest_data | + guest = guest_data[:guest] + if guest.present? + companion = view_context.companion(guest) + + host_data[:guests][:data] << { + name: guest.user.name, + area: (view_context._"forms.labels.generic.#{space}"), + email: guest.user.email, + arrival_departure: guest.arrival.present? && guest.departure.present? ? view_context.date_span(guest.arrival.to_date, guest.departure.to_date) : '', + companion: companion.present? ? (companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}")) : '', + city: guest.city, + food: (view_context._"articles.conference_registration.questions.food.#{guest.food}"), + allergies: guest.allergies + } + end + end + end + + @excel_data[:data] << host_data + end + return respond_to do | format | + format.xlsx { render xlsx: :stats, filename: "housing" } + end + end when :locations @locations = EventLocation.where(:conference_id => @this_conference.id) when :events diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9eae995..13d7b88 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1529,6 +1529,122 @@ module ApplicationHelper end end + def excel_table(excel_data) + format_xls 'table' do + workbook use_autowidth: true + format bg_color: '333333' + format 'td', font_name: 'Calibri', fg_color: '333333' + format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff' + format 'th.sub-table', font_name: 'Calibri', b: true, bg_color: 'DDDDDD', fg_color: '333333' + format 'td.datetime', num_fmt: 22, font_name: 'Courier New', sz: 10, fg_color: '333333' + format 'td.date.day', num_fmt: 14, font_name: 'Courier New', sz: 10, fg_color: '333333' + format 'td.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333' + format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true + end + + key = excel_data[:key] || 'excel.columns' + + content_tag(:table) do + (content_tag(:thead) do + content_tag(:tr, excel_header_columns(excel_data)) + end) + + content_tag(:tbody, excel_rows(excel_data)) + end + end + + def excel_header_columns(data, padding = {}, class_name = nil) + columns = '' + + data[:columns].each do |column| + unless data[:column_types].present? && data[:column_types][column] == :table + # columns += content_tag(:th, _(data[:keys][column].present? ? data[:keys][column] : "#{key}.#{column.to_s}"), class: class_name) + columns += content_tag(:th, data[:keys][column].present? ? _(data[:keys][column]) : '', class: class_name) + end + end + + pad_columns(columns, padding, :th) + end + + def excel_empty_row(data, padding = {}) + columns = '' + + data[:columns].each do |column| + unless data[:column_types].present? && data[:column_types][column] == :table + columns += content_tag(:td) + end + end + + content_tag(:tr, pad_columns(columns, padding)) + end + + def pad_columns(columns, padding, column_type = :td) + left = '' + + for i in 1..(padding['left'] || 0) + left += content_tag(:td) + end + + right = '' + for i in 1..(padding['right'] || 0) + right += content_tag(:td) + end + + (left + columns + right).html_safe + end + + def excel_columns(row, data, padding = {}) + columns = '' + + data[:columns].each do |column| + value = row[column].present? ? (_!row[column].to_s) : '' + class_name = nil + is_sub_table = false + + if data[:column_types].present? && data[:column_types][column].present? + if data[:column_types][column] == :table + is_sub_table = true + else + class_name = data[:column_types][column] + end + end + + columns += content_tag(:td, value, { class: class_name }) unless is_sub_table + end + + pad_columns(columns, padding) + end + + def excel_sub_tables(row, data, padding = {}) + rows = '' + + # shift the table right + new_padding = { + 'left' => (padding['right'] || 0) + 1, + 'right' => (padding['right'] || 0) - 1 + } + + data[:columns].each do |column| + if data[:column_types].present? && data[:column_types][column] == :table + puts row[column].to_json.to_s + rows += content_tag(:tr, excel_header_columns(row[column], new_padding, 'sub-table')) + rows += excel_rows(row[column], new_padding) + rows += excel_empty_row(row[column], new_padding) + end + + end + + rows.html_safe + end + + def excel_rows(data, padding = {}) + rows = '' + data[:data].each do |row| + rows += content_tag(:tr, excel_columns(row, data, padding)) + + excel_sub_tables(row, data, padding) + end + rows.html_safe + end + private def _original_content(value, lang) content_tag(:div, ( diff --git a/app/views/conferences/admin/_housing.html.haml b/app/views/conferences/admin/_housing.html.haml index 6b2442e..68ea8cb 100644 --- a/app/views/conferences/admin/_housing.html.haml +++ b/app/views/conferences/admin/_housing.html.haml @@ -5,3 +5,6 @@ = form_tag administration_update_path(@this_conference.slug, :housing), class: 'guest-dlg', id: 'guest-list-table' do %h3 Select a Guest #table +.actions + = link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, :housing, :format => :xlsx), class: [:button, :download] + diff --git a/app/views/conferences/admin/_stats.html.haml b/app/views/conferences/admin/_stats.html.haml index a1ed3f9..c9d0444 100644 --- a/app/views/conferences/admin/_stats.html.haml +++ b/app/views/conferences/admin/_stats.html.haml @@ -4,15 +4,15 @@ = data_set(:h4, 'articles.admin.stats.headings.incomplete_registrations') do = ((@registration_count - @completed_registrations) || 0).to_s = data_set(:h4, 'articles.admin.stats.headings.bikes') do - = (@completed_registrations || 0) > 0 ? "#{@bikes} (#{(@bikes / @completed_registrations.to_f).round(4) * 100.0}%)" : "0" + = (@completed_registrations || 0) > 0 ? "#{@bikes} (#{number_to_percentage(@bikes / @completed_registrations.to_f * 100.0)})" : "0" = data_set(:h4, 'articles.admin.stats.headings.food.meat') do - = (@food[:all] || 0) > 0 ? "#{@food[:meat]} (#{(@food[:meat] / @food[:all].to_f).round(4) * 100.0}%)" : "0" + = (@food[:all] || 0) > 0 ? "#{@food[:meat]} (#{number_to_percentage(@food[:meat] / @food[:all].to_f * 100.0)})" : "0" = data_set(:h4, 'articles.admin.stats.headings.food.vegetarian') do - = (@food[:all] || 0) > 0 ? "#{@food[:vegetarian]} (#{(@food[:vegetarian] / @food[:all].to_f).round(4) * 100.0}%)" : "0" + = (@food[:all] || 0) > 0 ? "#{@food[:vegetarian]} (#{number_to_percentage(@food[:vegetarian] / @food[:all].to_f * 100.0)})" : "0" = data_set(:h4, 'articles.admin.stats.headings.food.vegan') do - = (@food[:all] || 0) > 0 ? "#{@food[:vegan]} (#{(@food[:vegan] / @food[:all].to_f).round(4) * 100.0}%)" : "0" + = (@food[:all] || 0) > 0 ? "#{@food[:vegan]} (#{number_to_percentage(@food[:vegan] / @food[:all].to_f * 100.0)})" : "0" = data_set(:h4, 'articles.admin.stats.headings.donation_count') do - = (@completed_registrations || 0) > 0 ? "#{@donation_count} (#{(@donation_count / @completed_registrations.to_f).round(4) * 100.0}%)" : "0" + = (@completed_registrations || 0) > 0 ? "#{@donation_count} (#{number_to_percentage(@donation_count / @completed_registrations.to_f * 100.0)})" : "0" = data_set(:h4, 'articles.admin.stats.headings.donation_total') do = "$#{@donations || 0.00}" .actions diff --git a/app/views/conferences/stats.xlsx.haml b/app/views/conferences/stats.xlsx.haml index 7a3d277..7490ac9 100644 --- a/app/views/conferences/stats.xlsx.haml +++ b/app/views/conferences/stats.xlsx.haml @@ -1,21 +1 @@ -- key = @excel_data[:key] || 'excel.columns' -%table - %thead - %tr - - @excel_data[:columns].each do |column| - %th=_(@excel_data[:keys][column].present? ? @excel_data[:keys][column] : "#{key}.#{column.to_s}") - %tbody - - @excel_data[:data].each do |row| - %tr - - @excel_data[:columns].each do |column| - %td{class: (@excel_data[:column_types].present? && @excel_data[:column_types][column].present?) ? @excel_data[:column_types][column] : nil}=(row[column].present? ? (_!row[column]) : '') - -- format_xls 'table' do - - workbook use_autowidth: true - - format bg_color: '333333' - - format 'td', font_name: 'Calibri', fg_color: '333333' - - format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff' - - format 'td.datetime', num_fmt: 22, font_name: 'Courier New', sz: 10, fg_color: '333333' - - format 'td.date.day', num_fmt: 14, font_name: 'Courier New', sz: 10, fg_color: '333333' - - format 'td.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333' - - format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true += excel_table(@excel_data) diff --git a/config/locales/en.yml b/config/locales/en.yml index 208d157..3a18265 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1264,7 +1264,7 @@ en: subregion: State / Province country: Country postal_code: Postal Code - status: Status, + status: Status bike: Bike food: Food housing: Housing