Skip to content

Commit

Permalink
Provide CSV downloads of table contents
Browse files Browse the repository at this point in the history
For now, each table has to be downloaded individually. I'm trying to figure out if there's a way to combine all the files in a zip file and allow the user to download that.

Closes #304.
  • Loading branch information
monfresh committed Apr 26, 2015
1 parent b226c5c commit 9bf8887
Show file tree
Hide file tree
Showing 38 changed files with 621 additions and 14 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ gem 'dalli'
gem 'kgio'
gem 'memcachier'

gem 'csv_shaper'

group :production do
# Heroku recommended
gem 'rails_12factor'
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ GEM
simplecov (~> 0.9.1)
term-ansicolor (~> 1.3)
thor (~> 0.19.1)
csv_shaper (1.1.1)
activesupport (>= 3.0.0)
dalli (2.7.4)
database_cleaner (1.4.1)
debug_inspector (0.0.2)
Expand Down Expand Up @@ -335,6 +337,7 @@ DEPENDENCIES
capybara
coffee-rails (~> 4.1.0)
coveralls
csv_shaper
dalli
database_cleaner (>= 1.0.0.RC1)
devise (~> 3.4)
Expand Down
44 changes: 44 additions & 0 deletions app/controllers/admin/csv_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class Admin
class CsvController < ApplicationController
before_action :set_filename

# The CSV content for each action is defined in
# app/views/admin/csv/{action_name}.csv.shaper

def addresses
end

def contacts
end

def holiday_schedules
end

def locations
end

def mail_addresses
end

def organizations
end

def phones
end

def programs
end

def regular_schedules
end

def services
end

private

def set_filename
@filename = "All #{action_name} - #{Time.zone.today.to_formatted_s}.csv"
end
end
end
2 changes: 1 addition & 1 deletion app/models/category.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Category < ActiveRecord::Base
attr_accessible :name, :taxonomy_id

has_and_belongs_to_many :services, -> { uniq }
has_and_belongs_to_many :services

validates :name, :taxonomy_id,
presence: { message: I18n.t('errors.messages.blank_for_category') }
Expand Down
2 changes: 1 addition & 1 deletion app/models/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Service < ActiveRecord::Base
belongs_to :location, touch: true
belongs_to :program

has_and_belongs_to_many :categories, -> { order('taxonomy_id asc').uniq },
has_and_belongs_to_many :categories,
after_add: :touch_location,
after_remove: :touch_location

Expand Down
7 changes: 7 additions & 0 deletions app/views/admin/csv/addresses.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
csv.headers :id, :location_id, :address_1, :address_2, :city,
:state_province, :postal_code, :country

csv.rows Address.find_each do |csv, address|
csv.cells :id, :location_id, :address_1, :address_2, :city,
:state_province, :postal_code, :country
end
7 changes: 7 additions & 0 deletions app/views/admin/csv/contacts.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
csv.headers :id, :location_id, :organization_id, :service_id, :name, :title,
:email, :department

csv.rows Contact.find_each do |csv, contact|
csv.cells :id, :location_id, :organization_id, :service_id, :name, :title,
:email, :department
end
12 changes: 12 additions & 0 deletions app/views/admin/csv/holiday_schedules.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
csv.headers :id, :location_id, :service_id, :start_date, :end_date, :closed,
:opens_at, :closes_at

csv.rows HolidaySchedule.find_each do |csv, hs|
csv.cells :id, :location_id, :service_id, :start_date, :end_date, :closed,
:opens_at, :closes_at

csv.cell :start_date, hs.start_date.strftime('%B %d, %Y')
csv.cell :end_date, hs.end_date.strftime('%B %d, %Y')
csv.cell :opens_at, hs.opens_at.strftime('%H:%M') if hs.opens_at
csv.cell :closes_at, hs.closes_at.strftime('%H:%M') if hs.closes_at
end
15 changes: 15 additions & 0 deletions app/views/admin/csv/locations.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
csv.headers :id, :organization_id, :accessibility, :admin_emails,
:alternate_name, :description, :email, :languages,
:latitude, :longitude, :name, :short_desc, :transportation,
:website, :virtual

csv.rows Location.find_each do |csv, location|
csv.cells :id, :organization_id, :accessibility, :admin_emails,
:alternate_name, :description, :email, :languages,
:latitude, :longitude, :name, :short_desc, :transportation,
:website, :virtual

csv.cell :accessibility, location.accessibility.map(&:text).join(', ')
csv.cell :admin_emails, location.admin_emails.join(', ')
csv.cell :languages, location.languages.try(:join, ', ')
end
7 changes: 7 additions & 0 deletions app/views/admin/csv/mail_addresses.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
csv.headers :id, :location_id, :attention, :address_1, :address_2, :city,
:state_province, :postal_code, :country

csv.rows MailAddress.find_each do |csv, mail_address|
csv.cells :id, :location_id, :attention, :address_1, :address_2, :city,
:state_province, :postal_code, :country
end
14 changes: 14 additions & 0 deletions app/views/admin/csv/organizations.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
csv.headers :id, :accreditations, :alternate_name, :date_incorporated,
:description, :email, :funding_sources, :legal_status, :licenses,
:name, :tax_id, :tax_status, :website

csv.rows Organization.find_each do |csv, org|
csv.cells :id, :accreditations, :alternate_name, :date_incorporated,
:description, :email, :funding_sources, :legal_status, :licenses,
:name, :tax_id, :tax_status, :website

csv.cell :accreditations, org.accreditations.try(:join, ', ')
csv.cell :date_incorporated, org.date_incorporated.try(:strftime, '%B %d, %Y')
csv.cell :funding_sources, org.funding_sources.try(:join, ', ')
csv.cell :licenses, org.licenses.try(:join, ', ')
end
9 changes: 9 additions & 0 deletions app/views/admin/csv/phones.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
csv.headers :id, :contact_id, :location_id, :organization_id, :service_id,
:country_prefix, :department, :extension, :number, :number_type,
:vanity_number

csv.rows Phone.find_each do |csv, phone|
csv.cells :id, :contact_id, :location_id, :organization_id, :service_id,
:country_prefix, :department, :extension, :number, :number_type,
:vanity_number
end
6 changes: 6 additions & 0 deletions app/views/admin/csv/programs.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
csv.headers :id, :organization_id, :alternate_name, :name

csv.rows Program.find_each do |csv, _|
csv.cells :id, :organization_id, :alternate_name, :name
end

9 changes: 9 additions & 0 deletions app/views/admin/csv/regular_schedules.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
csv.headers :id, :location_id, :service_id, :weekday, :opens_at, :closes_at

csv.rows RegularSchedule.find_each do |csv, rs|
csv.cells :id, :location_id, :service_id, :weekday, :opens_at, :closes_at

csv.cell :opens_at, rs.opens_at.strftime('%H:%M')
csv.cell :closes_at, rs.closes_at.strftime('%H:%M')
csv.cell :weekday, rs.weekday == 7 ? 'Sunday' : Date::DAYNAMES[rs.weekday]
end
23 changes: 23 additions & 0 deletions app/views/admin/csv/services.csv.shaper
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
csv.headers :id, :location_id, :program_id, :accepted_payments,
:alternate_name, :application_process, :audience, :description,
:eligibility, :email, :fees, :funding_sources,
:interpretation_services, :keywords, :languages, :name,
:required_documents, :service_areas, :status, :wait_time, :website,
:taxonomy_ids

csv.rows Service.find_each do |csv, service|
csv.cells :id, :location_id, :program_id, :accepted_payments,
:alternate_name, :application_process, :audience, :description,
:eligibility, :email, :fees, :funding_sources,
:interpretation_services, :keywords, :languages, :name,
:required_documents, :service_areas, :status, :wait_time, :website,
:category_ids

csv.cell :accepted_payments, service.accepted_payments.try(:join, ', ')
csv.cell :funding_sources, service.funding_sources.try(:join, ', ')
csv.cell :keywords, service.keywords.try(:join, ', ')
csv.cell :languages, service.languages.try(:join, ', ')
csv.cell :required_documents, service.required_documents.try(:join, ', ')
csv.cell :service_areas, service.service_areas.try(:join, ', ')
csv.cell :taxonomy_ids, service.categories.pluck(:taxonomy_id).join(', ')
end
15 changes: 15 additions & 0 deletions app/views/admin/dashboard/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,18 @@
= link_to 'Add a new location', new_admin_location_path, class: 'btn btn-primary'
%p
= link_to 'Add a new program', new_admin_program_path, class: 'btn btn-primary'

- if current_admin.super_admin?
%h2 CSV Downloads
%p= link_to 'Download all addresses as CSV', admin_csv_addresses_path, class: 'btn btn-primary'
%p= link_to 'Download all contacts as CSV', admin_csv_contacts_path, class: 'btn btn-primary'
%p= link_to 'Download all holiday schedules as CSV', admin_csv_holiday_schedules_path, class: 'btn btn-primary'
%p= link_to 'Download all locations as CSV', admin_csv_locations_path, class: 'btn btn-primary'
%p= link_to 'Download all mail addresses as CSV', admin_csv_mail_addresses_path, class: 'btn btn-primary'
%p= link_to 'Download all organizations as CSV', admin_csv_organizations_path, class: 'btn btn-primary'
%p= link_to 'Download all phones as CSV', admin_csv_phones_path, class: 'btn btn-primary'
%p= link_to 'Download all programs as CSV', admin_csv_programs_path, class: 'btn btn-primary'
%p= link_to 'Download all regular schedules as CSV', admin_csv_regular_schedules_path, class: 'btn btn-primary'
%p= link_to 'Download all services as CSV', admin_csv_services_path, class: 'btn btn-primary'


3 changes: 3 additions & 0 deletions config/initializers/csv_shaper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CsvShaper.configure do |config|
config.header_inflector = :underscore
end
13 changes: 13 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@
resources :programs, except: :show
resources :services, only: :index

namespace :csv, defaults: { format: 'csv' } do
get 'addresses'
get 'contacts'
get 'holiday_schedules'
get 'locations'
get 'mail_addresses'
get 'organizations'
get 'phones'
get 'programs'
get 'regular_schedules'
get 'services'
end

get 'locations/:location_id/services/:id', to: 'services#edit'
get 'locations/:location_id/services/:service_id/contacts/:id', to: 'service_contacts#edit'
get 'locations/:location_id/contacts/:id', to: 'contacts#edit'
Expand Down
59 changes: 55 additions & 4 deletions spec/api/get_location_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
'email' => nil,
'fees' => nil,
'funding_sources' => [],
'application_process' => @location.services.first.application_process,
'application_process' => @location.services.first.application_process,
'interpretation_services' => @location.services.first.interpretation_services,
'keywords' => @location.services.first.keywords,
'languages' => [],
Expand All @@ -117,7 +117,7 @@
'phones' => [],
'regular_schedules' => [
{
'weekday' => 1,
'weekday' => 7,
'opens_at' => '2000-01-01T09:30:00.000Z',
'closes_at' => '2000-01-01T17:00:00.000Z'
}
Expand Down Expand Up @@ -159,7 +159,7 @@
{
'id' => @location.mail_address.id,
'attention' => @location.mail_address.attention,
'address_1' => @location.mail_address.address_1,
'address_1' => @location.mail_address.address_1,
'address_2' => nil,
'city' => @location.mail_address.city,
'state_province' => @location.mail_address.state_province,
Expand Down Expand Up @@ -206,7 +206,7 @@

serialized_regular_schedule =
{
'weekday' => 1,
'weekday' => 7,
'opens_at' => '2000-01-01T09:30:00.000Z',
'closes_at' => '2000-01-01T17:00:00.000Z'
}
Expand Down Expand Up @@ -272,4 +272,55 @@
end
end
end

describe 'ordering service categories by taxonomy_id' do
before :each do
@food = create(:category)
@food_child = @food.children.
create!(name: 'Community Gardens', taxonomy_id: '101-01')
@health = create(:health)
@health_child = @health.children.
create!(name: 'Orthodontics', taxonomy_id: '102-01')
create_service
@service.category_ids = [
@food.id, @food_child.id, @health.id, @health_child.id]
end

it 'orders the categories by taxonomy_id' do
get api_location_url(@location, subdomain: ENV['API_SUBDOMAIN'])

categories = [
{
'id' => @food.id,
'depth' => 0,
'taxonomy_id' => '101',
'name' => 'Food',
'parent_id' => nil
},
{
'id' => @food_child.id,
'depth' => 1,
'taxonomy_id' => '101-01',
'name' => 'Community Gardens',
'parent_id' => @food.id
},
{
'id' => @health.id,
'depth' => 0,
'taxonomy_id' => '102',
'name' => 'Health',
'parent_id' => nil
},
{
'id' => @health_child.id,
'depth' => 1,
'taxonomy_id' => '102-01',
'name' => 'Orthodontics',
'parent_id' => @health.id
}
]

expect(json['services'].first['categories']).to eq(categories)
end
end
end
6 changes: 6 additions & 0 deletions spec/factories/holiday_schedules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@
closed true
start_date 'December 24, 2014'
end_date 'December 24, 2014'

trait :open do
closed false
opens_at '9am'
closes_at '17:00'
end
end
end
1 change: 1 addition & 0 deletions spec/factories/organizations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
factory :org_with_extra_whitespace, class: Organization do
accreditations ['BBB ', ' AAA']
alternate_name 'AKA '
date_incorporated 'April 25, 2001'
description 'Organization created for testing purposes '
email '[email protected] '
funding_sources ['County ', ' State ']
Expand Down
2 changes: 1 addition & 1 deletion spec/factories/regular_schedules.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FactoryGirl.define do
factory :regular_schedule do
weekday 'Monday'
weekday 7
opens_at '9:30'
closes_at '5pm'
end
Expand Down
19 changes: 19 additions & 0 deletions spec/features/admin/csv/download_addresses_csv_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'rails_helper'

feature 'Downloading Addresses CSV' do
before do
@address = create(:address, location_id: 1)
visit admin_csv_addresses_path(format: 'csv')
end

it 'contains the same headers as in the import Wiki' do
expect(csv.first).to eq %w(id location_id address_1 address_2 city
state_province postal_code country)
end

it 'populates address attribute values' do
expect(csv.second).to eq [
@address.id.to_s, @address.location_id.to_s, '1800 Easton Drive', nil,
'Burlingame', 'CA', '94010', 'US']
end
end
Loading

0 comments on commit 9bf8887

Please sign in to comment.