├── .env.sample ├── .gitignore ├── .ruby-version ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── cable.js │ │ ├── channels │ │ │ ├── .keep │ │ │ ├── need.js │ │ │ └── shelter.js │ │ ├── components │ │ │ └── place_autocomplete.js │ │ ├── connect │ │ │ ├── haves.js │ │ │ └── needs.js │ │ ├── notifications.js │ │ └── simple_map.js │ └── stylesheets │ │ ├── application.css │ │ ├── connect │ │ ├── haves.scss │ │ └── needs.scss │ │ ├── main.css │ │ ├── notifications.scss │ │ ├── pages.scss │ │ └── splash.scss ├── channels │ ├── application_cable │ │ ├── channel.rb │ │ └── connection.rb │ ├── need_channel.rb │ └── shelter_channel.rb ├── controllers │ ├── amazon_products_controller.rb │ ├── api │ │ ├── v1 │ │ │ ├── amazon_products_controller.rb │ │ │ ├── charitable_organizations_controller.rb │ │ │ ├── connect │ │ │ │ ├── categories_controller.rb │ │ │ │ ├── flags_controller.rb │ │ │ │ └── markers_controller.rb │ │ │ ├── needs_controller.rb │ │ │ └── shelters_controller.rb │ │ └── v2 │ │ │ └── locations_controller.rb │ ├── application_controller.rb │ ├── charitable_organizations_controller.rb │ ├── concerns │ │ ├── .keep │ │ └── device_identifiable.rb │ ├── connect │ │ └── flagged_markers_controller.rb │ ├── drafts_controller.rb │ ├── location_drafts_controller.rb │ ├── locations_controller.rb │ ├── needs_controller.rb │ ├── organizations_controller.rb │ ├── pages_controller.rb │ ├── shelters_controller.rb │ ├── splash_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── connect │ │ ├── haves_helper.rb │ │ └── needs_helper.rb │ ├── mucked_homes_helper.rb │ ├── pages_helper.rb │ ├── splash_helper.rb │ └── volunteers_helper.rb ├── importers │ └── api_importer.rb ├── jobs │ ├── application_job.rb │ ├── fetch_amazon_product_job.rb │ ├── import_needs_job.rb │ ├── import_shelters_job.rb │ ├── need_update_notifier_job.rb │ ├── schedule_amazon_fetch_job.rb │ └── shelter_update_notifier_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── amazon_product.rb │ ├── application_record.rb │ ├── charitable_organization.rb │ ├── concerns │ │ └── .keep │ ├── connect.rb │ ├── connect │ │ └── marker.rb │ ├── draft.rb │ ├── ignored_amazon_product_need.rb │ ├── location.rb │ ├── location │ │ ├── config.rb │ │ ├── hhrd │ │ │ ├── pois.rb │ │ │ ├── rescuees.rb │ │ │ └── rescuers.rb │ │ └── whitelist.rb │ ├── mucked_home.rb │ ├── need.rb │ ├── page.rb │ ├── shelter.rb │ ├── user.rb │ └── volunteer.rb └── views │ ├── amazon_products │ ├── _form.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ └── show.html.erb │ ├── api │ ├── v1 │ │ ├── amazon_products │ │ │ ├── _amazon_product.json.jbuilder │ │ │ └── index.json.jbuilder │ │ ├── charitable_organizations │ │ │ ├── _charitable_organization.json.jbuilder │ │ │ └── index.json.jbuilder │ │ ├── connect │ │ │ └── markers │ │ │ │ ├── index.json.jbuilder │ │ │ │ └── show.json.jbuilder │ │ ├── needs │ │ │ ├── _need.json.jbuilder │ │ │ └── index.json.jbuilder │ │ └── shelters │ │ │ ├── _shelter.json.jbuilder │ │ │ └── index.json.jbuilder │ └── v2 │ │ └── locations │ │ ├── _location.json.jbuilder │ │ ├── help.json.jbuilder │ │ ├── index.json.jbuilder │ │ └── routes.json.jbuilder │ ├── charitable_organizations │ ├── _form.html.erb │ ├── drafts.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── connect │ └── flagged_markers │ │ ├── index.html.erb │ │ └── show.html.erb │ ├── drafts │ └── show.html.erb │ ├── layouts │ ├── application.html.erb │ ├── locations.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── location_drafts │ └── show.html.erb │ ├── locations │ ├── _form.html.erb │ ├── drafts.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── needs │ ├── _form.html.erb │ ├── drafts.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── organizations │ └── show.html.erb │ ├── pages │ ├── _form.html.erb │ ├── _page.html.erb │ ├── edit.html.erb │ └── index.html.erb │ ├── shared │ ├── _drafts_table.html.erb │ ├── _show.html.erb │ └── _table.html.erb │ ├── shelters │ ├── _form.html.erb │ ├── drafts.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── splash │ └── index.html.erb │ └── users │ ├── index.html.erb │ ├── show.html.erb │ └── update.html.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── connect_categories.yml ├── database.yml ├── database.yml.travis ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── geocoder.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── simple_form.rb │ ├── traffic_cop.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ ├── en.yml │ └── simple_form.en.yml ├── puma.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── db ├── migrate │ ├── 20170831193310_create_shelters.rb │ ├── 20170831193314_create_needs.rb │ ├── 20170831220720_change_lat_long_to_floats.rb │ ├── 20170901185804_add_update_draft_table.rb │ ├── 20170901194923_devise_create_users.rb │ ├── 20170901201514_create_amazon_products.rb │ ├── 20170901224459_add_accept_deny_draft.rb │ ├── 20170902123713_add_disabled_to_amazon_products.rb │ ├── 20170902140452_create_ignored_amazon_product_needs.rb │ ├── 20170902203116_add_archive_to_need.rb │ ├── 20170902213139_create_mucked_homes.rb │ ├── 20170902222739_create_volunteers.rb │ ├── 20170903113333_create_connect_markers.rb │ ├── 20170903230413_create_charitable_organizations.rb │ ├── 20170903231428_add_archive_to_charitable_organizations.rb │ ├── 20170904025322_add_email_to_connect_markers.rb │ ├── 20170904165336_add_product_categories_to_amazon_products.rb │ ├── 20170905114546_add_data_to_markers.rb │ ├── 20170905201043_create_pages.rb │ ├── 20170906160409_add_amazon_price_to_products.rb │ ├── 20170907032253_add_fields_to_shelter.rb │ ├── 20170907070934_add_location_table.rb │ ├── 20170907152426_add_device_uuid_connect_markers.rb │ ├── 20170908152859_update_category_on_connect_markers.rb │ ├── 20170911052811_add_state_to_shelter.rb │ ├── 20170911205637_add_indexes.rb │ ├── 20170911210028_add_calculated_values_for_shelters.rb │ └── 20170920210424_add_index_to_data_on_connect_markers.rb ├── schema.rb ├── seeds.rb └── seeds │ ├── amazon_products.json │ └── ignored_amazon_product_needs.json ├── lib ├── assets │ └── .keep ├── tasks │ ├── .keep │ ├── amazon.rake │ ├── data.rake │ └── import.rake └── templates │ └── erb │ └── scaffold │ └── _form.html.erb ├── log └── .keep ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── api-docs │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── index.html │ ├── oauth2-redirect.html │ ├── swagger-ui-bundle.js │ ├── swagger-ui-bundle.js.map │ ├── swagger-ui-standalone-preset.js │ ├── swagger-ui-standalone-preset.js.map │ ├── swagger-ui.css │ ├── swagger-ui.css.map │ ├── swagger-ui.js │ ├── swagger-ui.js.map │ ├── swagger-v1.json │ └── swagger-v2.json ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── contributors.html ├── favicon.ico ├── images │ └── readme │ │ ├── screenshot_create-service-account-key.png │ │ ├── screenshot_enable_google_sheets_api.png │ │ └── screenshot_rails_server_run_test.png └── robots.txt ├── test ├── application_system_test_case.rb ├── controllers │ ├── .keep │ ├── amazon_products_controller_test.rb │ ├── api │ │ └── v1 │ │ │ ├── amazon_product_needs_controller_test.rb │ │ │ ├── charitable_organizations_controller_test.rb │ │ │ ├── connect │ │ │ ├── categories_controller_test.rb │ │ │ ├── flags_controller_test.rb │ │ │ └── markers_controller_test.rb │ │ │ ├── needs_controller_test.rb │ │ │ └── shelters_controller_test.rb │ ├── charitable_organizations_controller_test.rb │ ├── connect │ │ └── flagged_markers_controller_test.rb │ ├── drafts_controller_test.rb │ ├── location_drafts_controller_test.rb │ ├── locations_controller_test.rb │ ├── needs_controller_test.rb │ ├── pages_controller_test.rb │ └── shelters_controller_test.rb ├── fixtures │ ├── .keep │ ├── amazon_products.yml │ ├── charitable_organizations.yml │ ├── connect │ │ └── markers.yml │ ├── drafts.yml │ ├── files │ │ └── .keep │ ├── locations.yml │ ├── mucked_homes.yml │ ├── needs.yml │ ├── pages.yml │ ├── shelters.yml │ ├── users.yml │ └── volunteers.yml ├── helpers │ └── .keep ├── integration │ └── .keep ├── jobs │ ├── fetch_amazon_product_job_test.rb │ ├── import_needs_job_test.rb │ ├── import_sheet_data_job_test.rb │ ├── import_shelters_job_test.rb │ └── schedule_amazon_fetch_job_test.rb ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── amazon_product_test.rb │ ├── charitable_organization_test.rb │ ├── connect │ │ └── marker_test.rb │ ├── ignored_amazon_product_need_test.rb │ ├── location_test.rb │ ├── mucked_home_test.rb │ ├── need_test.rb │ ├── page_test.rb │ ├── shelter_test.rb │ ├── user_test.rb │ └── volunteer_test.rb ├── system │ ├── .keep │ ├── admin_searchings_test.rb │ └── homepage_test.rb ├── test_helper.rb └── vcr_cassettes │ ├── amazon-missing.yml │ ├── amazon.yml │ ├── needs.yml │ └── shelters.yml ├── tmp └── .keep └── vendor └── .keep /.env.sample: -------------------------------------------------------------------------------- 1 | AWS_ACCESS_KEY_ID="AKIAJ5PESCDQX7KIMQ5Q" 2 | AWS_SECRET_ACCESS_KEY="" 3 | AWS_ASSOCIATE_TAG=oneclickrelie-20 4 | GOOGLE_GEOCODER_API_KEY="" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | /node_modules 17 | /yarn-error.log 18 | 19 | .byebug_history 20 | .env 21 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | rvm: 4 | - 2.4.1 5 | addons: 6 | postgresql: '9.6' 7 | sources: 8 | - google-chrome 9 | apt: 10 | packages: 11 | - google-chrome-stable 12 | services: 13 | - redis-server 14 | before_script: 15 | - psql -c 'create database travis_ci_test;' -U postgres 16 | - cp config/database.yml.travis config/database.yml 17 | - wget http://chromedriver.storage.googleapis.com/2.27/chromedriver_linux64.zip 18 | - unzip chromedriver_linux64.zip 19 | - sudo apt-get install libnss3 20 | - sudo apt-get --only-upgrade install google-chrome-stable 21 | - sudo cp chromedriver /usr/local/bin/. 22 | - sudo chmod +x /usr/local/bin/chromedriver 23 | - export DISPLAY=:99.0 24 | - sh -e /etc/init.d/xvfb start 25 | - sleep 3 26 | script: 27 | - RAILS_ENV=test bundle exec rails db:migrate --trace 28 | - bundle exec rails db:test:prepare 29 | - bundle exec rails test 30 | - bundle exec rails test:system 31 | deploy: 32 | - provider: heroku 33 | api_key: 34 | secure: jheoABY2Yhe+eP4DB87h877u4cSc02Rf09PhmRxNroQQHv3XkToVgxXo33TEEuCoucDfhSepOeky4QozarJ7hSHCn1ssPb8jo3klicxPrB90t8SBPHB7evqfcaXAGYZBIPcvoGXzqQ0Brv/IvSP2eK81KUFAA8CAE9jUI4HXJkqRWnL2SC51yytxhnTBruOlTA3Z/90vgj9q6y3Y+xZCP3DrBDoBlvdaeyOXt54X+vcj+ZKepiMTdOuSEkC8dxuLiru7yMWZPANvvP/B/MLnh6XdG/EggRujR/uKvfrRKjKZjRt/TkMM7SarP+klGqOiaHV9KOct28E2Wbmoe03Qllk2NopqsanF4wemUWb8jCp4fLaf6e6KPnzA+Vfr3C8h1AyJYAXDXKx+jAa3pGDSSuDQmvO9lNtpLpxNbVwHSpBvdaY9MDta64oj+5eb2RR/UZZmB8JBuW2+2B22cf/OPU/Z915qWzshE3Fi0pTEgca4Tq37KqzyuYl8n4JnLxsUIWYVytWMMX8JtnvMYJdVLJEWrNidAD/BrZQGRhKUE8vnTMK7sFCzC2XInNpFXXDW0z2Keor8tb0Qk0y+PUyUlwy6YJgmITGkBHcRNWM0tYwO8+DS/duqvlSg0gMdxE6sa6mz9eVk1oP7KBJ/O4VdvJoRkdMiMKC9UvCceLqAT5A= 35 | app: harvey-api 36 | on: 37 | repo: sketch-city/harvey-api 38 | run: 39 | - "rails db:migrate" 40 | - "rails calculate_values" 41 | - "rake cleanup" 42 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby "2.4.1" 4 | 5 | git_source(:github) do |repo_name| 6 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 7 | "https://github.com/#{repo_name}.git" 8 | end 9 | 10 | gem 'rails', '~> 5.1.1' 11 | gem 'pg', '~> 0.18' 12 | gem 'puma', '~> 3.7' 13 | gem 'sass-rails', '~> 5.0' 14 | gem 'uglifier', '>= 1.3.0' 15 | gem 'jbuilder', '~> 2.5' 16 | gem 'google-api-client', '~> 0.11' 17 | gem 'sucker_punch' 18 | gem 'activejob-traffic_control' 19 | gem 'dotenv-rails' 20 | gem 'rack-cors', :require => 'rack/cors' 21 | gem 'geocoder' 22 | gem 'devise' 23 | gem 'vacuum' 24 | gem 'connection_pool' 25 | gem "httparty", "~> 0.15.6" 26 | gem 'redis', '~> 3.2' 27 | gem 'simple_form' 28 | gem 'redcarpet' 29 | 30 | group :development, :test do 31 | gem 'capybara', '~> 2.13' 32 | gem 'selenium-webdriver' 33 | gem 'pry' 34 | gem 'vcr' 35 | gem "webmock", "~> 3.0" 36 | end 37 | 38 | group :development do 39 | gem 'web-console', '>= 3.3.0' 40 | gem 'listen', '>= 3.0.5', '< 3.2' 41 | gem 'spring' 42 | gem 'spring-watcher-listen', '~> 2.0.0' 43 | end 44 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require rails-ujs 2 | //= require_tree . 3 | //= require_tree ./components 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | //= require action_cable 2 | //= require_self 3 | //= require_tree ./channels 4 | 5 | ;(function() { 6 | this.App || (this.App = {}) 7 | 8 | App.cable = ActionCable.createConsumer() 9 | }.call(this)) 10 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/channels/need.js: -------------------------------------------------------------------------------- 1 | App.need = App.cable.subscriptions.create("NeedChannel", { 2 | connected: function() {}, 3 | received: function(data) { 4 | App.updatedNeeds.add(data.need) 5 | } 6 | }) 7 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/shelter.js: -------------------------------------------------------------------------------- 1 | App.shelter = App.cable.subscriptions.create("ShelterChannel", { 2 | connected: function() {}, 3 | received: function(data) { 4 | App.updatedShelters.add(data.shelter) 5 | } 6 | }) 7 | -------------------------------------------------------------------------------- /app/assets/javascripts/connect/haves.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/connect/needs.js: -------------------------------------------------------------------------------- 1 | // Place all the behaviors and hooks related to the matching controller here. 2 | // All this logic will automatically be available in application.js. 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/notifications.js: -------------------------------------------------------------------------------- 1 | function SubscribableArray() { 2 | return { 3 | _items: [], 4 | _callbacks: [], 5 | clear: function() { 6 | this._items = [] 7 | this._publish() 8 | }, 9 | add: function(shelter) { 10 | this._items.push(shelter) 11 | this._publish() 12 | }, 13 | subscribe: function(callback) { 14 | this._callbacks.push(callback) 15 | }, 16 | _publish: function() { 17 | const shelters = this._items 18 | this._callbacks.forEach(function(cb) { 19 | cb(shelters) 20 | }) 21 | } 22 | } 23 | } 24 | 25 | ;(function() { 26 | this.App || (this.App = {}) 27 | 28 | App.updatedShelters = new SubscribableArray() 29 | App.updatedNeeds = new SubscribableArray() 30 | }.call(this)) 31 | -------------------------------------------------------------------------------- /app/assets/javascripts/simple_map.js: -------------------------------------------------------------------------------- 1 | function simpleMap(options) { 2 | var selector = options.selector 3 | var name = options.name || "Location" 4 | var lat = options.lat 5 | var lng = options.lng 6 | 7 | var element = document.querySelector(selector) 8 | if (!!element) { 9 | var map = new google.maps.Map(element, { 10 | zoom: 12, 11 | center: { lat: lat, lng: lng } 12 | }) 13 | 14 | var marker = new google.maps.Marker({ 15 | position: { lat: lat, lng: lng }, 16 | map: map, 17 | title: name 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/connect/haves.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the connect/haves controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/connect/needs.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the connect/needs controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/notifications.scss: -------------------------------------------------------------------------------- 1 | .new-record-notification { 2 | text-align: right; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the pages controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/splash.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the splash controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/need_channel.rb: -------------------------------------------------------------------------------- 1 | class NeedChannel < ApplicationCable::Channel 2 | def subscribed 3 | stream_for "needs" 4 | end 5 | 6 | def unsubscribed 7 | # Any cleanup needed when channel is unsubscribed 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/channels/shelter_channel.rb: -------------------------------------------------------------------------------- 1 | class ShelterChannel < ApplicationCable::Channel 2 | def subscribed 3 | stream_for "shelters" 4 | end 5 | 6 | def unsubscribed 7 | # Any cleanup needed when channel is unsubscribed 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/amazon_products_controller.rb: -------------------------------------------------------------------------------- 1 | class AmazonProductsController < ApplicationController 2 | before_action :authenticate_admin! 3 | before_action :set_headers 4 | 5 | def index 6 | @amazon_products = AmazonProduct.all 7 | end 8 | 9 | def show 10 | @amazon_product = AmazonProduct.find params[:id] 11 | end 12 | 13 | def edit 14 | @amazon_product = AmazonProduct.find params[:id] 15 | end 16 | 17 | def update 18 | @amazon_product = AmazonProduct.find params[:id] 19 | if @amazon_product.update amazon_product_params 20 | redirect_to @amazon_product, notice: 'Shelter was successfully created.' 21 | else 22 | render :edit 23 | end 24 | end 25 | 26 | private 27 | def set_headers 28 | @columns = AmazonProduct::ColumnNames 29 | @headers = AmazonProduct::HeaderNames 30 | end 31 | 32 | def amazon_product_params 33 | params.require(:amazon_product).permit(AmazonProduct::UpdateFields).keep_if { |_,v| v.present? } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/controllers/api/v1/amazon_products_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::AmazonProductsController < ApplicationController 2 | 3 | before_action do 4 | request.format = :json 5 | end 6 | 7 | def index 8 | @filters = {} 9 | # get all needs into a nice array 10 | # find all Amazon products we have 11 | @needs = Need.all.map(&:clean_needs).flatten.uniq 12 | 13 | if params[:need].present? 14 | @filters[:need] = params[:need] 15 | @needs = @needs.select{|need| need =~ /#{params[:need]}/i } 16 | end 17 | 18 | @products = AmazonProduct 19 | .active 20 | .where(need: @needs) 21 | .order("priority, need") 22 | 23 | if params[:priority].to_s == "true" 24 | @filters[:priority] = params[:priority] 25 | @products = @products.priority 26 | end 27 | 28 | if params[:category].present? 29 | @filters[:category] = params[:category] 30 | @products = @products 31 | .where("category_specific ILIKE ?", "%#{params[:category]}%") 32 | .or(@products.where("category_general ILIKE ?", "%#{params[:category]}%")) 33 | end 34 | 35 | if params[:limit].to_i > 0 36 | @filters[:limit] = params[:limit].to_i 37 | @products = @products.limit(params[:limit].to_i) 38 | end 39 | 40 | fresh_when(etag: @products, last_modified: @products.maximum(:updated_at), public: true) 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/controllers/api/v1/charitable_organizations_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::CharitableOrganizationsController < ApplicationController 2 | 3 | before_action do 4 | request.format = :json 5 | end 6 | 7 | def index 8 | @filters = {} 9 | # get all charitable_organizations into a nice array 10 | # find all Amazon products we have 11 | @charitable_organizations = CharitableOrganization.all 12 | 13 | if params[:services].present? 14 | @filters[:services] = params[:services] 15 | @charitable_organizations = @charitable_organizations.where("services ILIKE ?", "%#{params[:services]}%") 16 | end 17 | 18 | if params[:name].present? 19 | @filters[:name] = params[:name] 20 | @charitable_organizations = @charitable_organizations.where("name ILIKE ?", "%#{params[:name]}%") 21 | end 22 | 23 | if params[:food_bank].present? 24 | @filters[:food_bank] = params[:food_bank] 25 | @charitable_organizations = @charitable_organizations.where(food_bank: true) 26 | end 27 | 28 | if params[:city].present? 29 | @filters[:city] = params[:city] 30 | @charitable_organizations = @charitable_organizations.where("city ILIKE ?", "%#{params[:city]}%") 31 | end 32 | 33 | if stale?(etag: @charitable_organizations, last_modified: @charitable_organizations.maximum(:updated_at), public: true) 34 | 35 | # here because limit is causing a SQL problem: column "distance" does not exist 36 | if params[:limit].to_i > 0 37 | @filters[:limit] = params[:limit].to_i 38 | @charitable_organizations = @charitable_organizations.take(params[:limit].to_i) 39 | end 40 | 41 | end 42 | 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/api/v1/connect/categories_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | module V1 5 | module Connect 6 | class CategoriesController < ApplicationController 7 | def index 8 | render json: categories 9 | end 10 | 11 | private 12 | 13 | def categories 14 | @categories ||= Rails.application.config_for(:connect_categories) 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/api/v1/connect/flags_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | module V1 5 | module Connect 6 | class FlagsController < ApplicationController 7 | include DeviceIdentifiable 8 | 9 | def create 10 | @marker = ::Connect::Marker.find(params[:marker_id]) 11 | @marker.flag_as_inappropriate!(device_uuid: device_uuid, reason: params[:reason]) 12 | head :no_content 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/api/v1/needs_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::NeedsController < ApplicationController 2 | 3 | before_action do 4 | request.format = :json 5 | end 6 | 7 | def index 8 | @filters = {} 9 | @needs = Need.all 10 | 11 | if params[:lat].present? && params[:lon].present? 12 | @filters[:lon] = params[:lon] 13 | @filters[:lat] = params[:lat] 14 | @needs = @needs.near([params[:lat], params[:lon]], 100) 15 | end 16 | 17 | if params[:location_name].present? 18 | @filters[:location_name] = params[:location_name] 19 | @needs = @needs.where("location_name ILIKE ?", "%#{params[:location_name]}%") 20 | end 21 | 22 | if params[:volunteers_needed].present? 23 | @filters[:volunteers_needed] = params[:volunteers_needed] 24 | @needs = @needs.where(are_volunteers_needed: true) 25 | end 26 | 27 | if params[:supplies_needed].present? 28 | @filters[:supplies_needed] = params[:supplies_needed] 29 | @needs = @needs.where(are_supplies_needed: true) 30 | end 31 | 32 | 33 | if stale?(etag: @needs, last_modified: @needs.maximum(:updated_at), public: true) 34 | 35 | # here because limit is causing a SQL problem: column "distance" does not exist 36 | if params[:limit].to_i > 0 37 | @filters[:limit] = params[:limit].to_i 38 | @needs = @needs.take(params[:limit].to_i) 39 | end 40 | 41 | end 42 | 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /app/controllers/api/v1/shelters_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::SheltersController < ApplicationController 2 | 3 | before_action do 4 | request.format = :json 5 | end 6 | 7 | def index 8 | @filters = {} 9 | @shelters = Shelter.all 10 | 11 | if params[:lat].present? && params[:lon].present? 12 | @filters[:lon] = params[:lon] 13 | @filters[:lat] = params[:lat] 14 | @shelters = @shelters.near([params[:lat], params[:lon]], 100, order: "distance") 15 | end 16 | 17 | if params[:county].present? 18 | @filters[:county] = params[:county] 19 | @shelters = @shelters.where("county ILIKE ?", "%#{@filters[:county]}%") 20 | end 21 | 22 | if params[:shelter].present? 23 | @filters[:shelter] = params[:shelter] 24 | @shelters = @shelters.where("shelter ILIKE ?", "%#{@filters[:shelter]}%") 25 | end 26 | 27 | if params[:accepting].present? 28 | @filters[:accepting] = params[:accepting] 29 | @shelters = @shelters.where(accepting: true) 30 | end 31 | 32 | if stale?(etag: @shelters, last_modified: @shelters.maximum(:updated_at), public: true) 33 | 34 | # here because limit is causing a SQL problem: column "distance" does not exist 35 | if params[:limit].to_i > 0 36 | @filters[:limit] = params[:limit].to_i 37 | @shelters = @shelters.take(params[:limit].to_i) 38 | end 39 | 40 | end 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /app/controllers/api/v2/locations_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V2::LocationsController < ApplicationController 2 | before_action :setup, except: [:routes] 3 | 4 | before_action do 5 | request.format = :json 6 | end 7 | 8 | def setup 9 | @organization = params[:organization] 10 | @legacy_table_name = params[:legacy_table_name] 11 | @location_class ||= Location::Whitelist.find(@organization, @legacy_table_name) 12 | @locations ||= Location::Whitelist.find(@organization, @legacy_table_name).all 13 | end 14 | 15 | def index 16 | 17 | @filters = {} 18 | 19 | @locations.filters.values.each do |filter| 20 | case filter.type 21 | when :coordinates 22 | if params[:lat].present? && params[:lon].present? 23 | @filters[:lon] = params[:lon] 24 | @filters[:lat] = params[:lat] 25 | @locations = @locations.near([params[:lat], params[:lon]], 100) 26 | end 27 | when :limit 28 | if params[:limit].to_i > 0 29 | @locations = @locations.limit(params[:limit].to_i) 30 | end 31 | when :truthy 32 | if params[filter.name].present? 33 | @filters[filter.name] = params[filter.name] 34 | 35 | if(filter.column) 36 | @locations = @locations.where(filter.name => true) 37 | else 38 | @locations = @locations.where("(legacy_data->>?)::boolean", filter.name) 39 | end 40 | end 41 | else 42 | if params[filter.name].present? 43 | @filters[filter.name] = params[filter.name] 44 | 45 | # Location whitelists valid columns. No sql injection here. 46 | if(filter.column) 47 | @locations = @locations.where("#{filter.name} ILIKE ?", "%#{@filters[filter.name]}%") 48 | else 49 | @locations = @locations.where("legacy_data->>? ILIKE ?", filter.name, "%#{@filters[filter.name]}%") 50 | end 51 | end 52 | end 53 | end 54 | 55 | def help 56 | end 57 | 58 | def routes 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :null_session 3 | 4 | before_action do 5 | @application_name = ENV.fetch('CANONICAL_NAME', 'Disaster API') 6 | end 7 | 8 | def admin? 9 | user_signed_in? && current_user.admin? 10 | end 11 | 12 | def authenticate_admin! 13 | if !admin? 14 | redirect_to request.referrer || root_path, notice: "Admins Only! :|" 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/charitable_organizations_controller.rb: -------------------------------------------------------------------------------- 1 | class CharitableOrganizationsController < ApplicationController 2 | before_action :set_headers 3 | before_action :set_charitable_organization, only: [:show, :edit, :update, :destroy, :archive] 4 | 5 | def index 6 | @charitable_organizations = CharitableOrganization.all 7 | end 8 | 9 | def new 10 | @charitable_organization = CharitableOrganization.new 11 | end 12 | 13 | def create 14 | 15 | if(user_signed_in? && current_user.admin?) 16 | @charitable_organization = CharitableOrganization.new(charitable_organization_update_params) 17 | 18 | if @charitable_organization.save 19 | redirect_to @charitable_organization, notice: 'Charitable Organization was successfully created.' 20 | else 21 | render :new 22 | end 23 | else 24 | draft = Draft.new(info: charitable_organization_update_params.merge({record_type: CharitableOrganization.name})) 25 | 26 | if draft.save 27 | redirect_to draft, notice: 'Your new Charitable Organization is pending approval.' 28 | else 29 | @charitable_organization = CharitableOrganization.new(charitable_organization_update_params) 30 | render :new 31 | end 32 | end 33 | end 34 | 35 | def show 36 | @charitable_organization = CharitableOrganization.find(params[:id]) 37 | end 38 | 39 | def destroy 40 | end 41 | 42 | def archive 43 | if(user_signed_in? && current_user.admin?) 44 | @charitable_organization.update_attributes(active: false) 45 | redirect_to charitable_organizations_path, notice: "Archived!" 46 | else 47 | redirect_to charitable_organizations_path, notice: "You must be an admin to archive." 48 | end 49 | end 50 | 51 | def edit 52 | end 53 | 54 | def update 55 | if(user_signed_in? && current_user.admin?) 56 | if @charitable_organization.update(charitable_organization_update_params) 57 | redirect_to @charitable_organization, notice: 'Charitable Organization was successfully updated.' 58 | else 59 | render :edit 60 | end 61 | else 62 | draft = Draft.new(record: @charitable_organization, info: charitable_organization_update_params) 63 | 64 | if draft.save 65 | redirect_to draft, notice: 'Your Charitable Organization update is pending approval.' 66 | else 67 | render :edit 68 | end 69 | end 70 | end 71 | 72 | 73 | def drafts 74 | @drafts = Draft.includes(:record).where("record_type = ? OR info->>'record_type' = 'CharitableOrganization'", CharitableOrganization.name).where(accepted_by_id: nil).where(denied_by_id: nil) 75 | end 76 | 77 | def set_headers 78 | @columns = CharitableOrganization::ColumnNames 79 | @headers = CharitableOrganization::HeaderNames 80 | end 81 | 82 | def set_charitable_organization 83 | @charitable_organization = CharitableOrganization.find(params[:id]) 84 | end 85 | 86 | def charitable_organization_update_params 87 | params.require(:charitable_organization).permit(CharitableOrganization::UpdateFields).keep_if { |_,v| v.present? } 88 | end 89 | 90 | end 91 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/concerns/device_identifiable.rb: -------------------------------------------------------------------------------- 1 | module DeviceIdentifiable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_action :check_device_uuid, only: [:create, :update] 6 | end 7 | 8 | private 9 | 10 | def device_uuid 11 | request.headers['HTTP_DISASTERCONNECT_DEVICE_UUID'] 12 | end 13 | 14 | def missing_device_uuid 15 | render(json: { device_uuid: ['Must be set with DisasterConnect-Device-UUID header'] }, status: :forbidden) 16 | end 17 | 18 | def check_device_uuid 19 | missing_device_uuid if device_uuid.blank? 20 | end 21 | end -------------------------------------------------------------------------------- /app/controllers/connect/flagged_markers_controller.rb: -------------------------------------------------------------------------------- 1 | class Connect::FlaggedMarkersController < ApplicationController 2 | before_action :authenticate_admin! 3 | helper_method :fields 4 | 5 | def index 6 | logger.info current_user 7 | @flagged_markers = ::Connect::Marker.unresolved.flagged 8 | end 9 | 10 | def show 11 | @flagged_marker = ::Connect::Marker.find(params[:id]) 12 | end 13 | 14 | def update 15 | ::Connect::Marker.find(params[:id]).update!(resolved: true) 16 | logger.info "#{current_user.email} confirmed marker[#{params[:id]}] as inappropriate." 17 | redirect_to connect_flagged_markers_url, notice: 'Marker confirmed.' 18 | end 19 | 20 | def destroy 21 | ::Connect::Marker.find(params[:id]).clear_inappropriate_flag! 22 | logger.info "#{current_user.email} cleared marker[#{params[:id]}]." 23 | redirect_to connect_flagged_markers_url, notice: 'Marker cleared.' 24 | end 25 | 26 | private 27 | 28 | def fields 29 | %w(flagged_at flagged_for marker_type name address) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/drafts_controller.rb: -------------------------------------------------------------------------------- 1 | class DraftsController < ApplicationController 2 | before_action :authenticate_admin!, only: [:destroy, :accept] 3 | before_action :set_record 4 | 5 | def show 6 | @columns = @record.class::ColumnNames 7 | @headers = @record.class::HeaderNames 8 | end 9 | 10 | def destroy 11 | @draft.update(denied_by: current_user) 12 | redirect_to @draft.record || root_path, notice: "#{@record.class.name} update was denied." 13 | end 14 | 15 | def accept 16 | info = @draft.info.delete_if { |k,_| "record_type" == k } 17 | 18 | @record.assign_attributes(info) 19 | 20 | if(@record.save) 21 | @draft.update(record: @record, accepted_by: current_user) 22 | redirect_to [:drafts, @record.class.name.underscore.pluralize.to_sym], notice: "#{@record.class.name} updated" 23 | else 24 | flash[:notice] = "Something went wrong." 25 | render :edit 26 | end 27 | end 28 | 29 | private 30 | 31 | def set_record 32 | @draft = Draft.find(params[:id]) 33 | if(@draft.record) 34 | @record = @draft.record 35 | else 36 | @record = @draft.info["record_type"].constantize.new 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/controllers/location_drafts_controller.rb: -------------------------------------------------------------------------------- 1 | class LocationDraftsController < ApplicationController 2 | before_action :authenticate_admin!, only: [:destroy, :accept] 3 | before_action :set_record 4 | 5 | layout "locations" 6 | 7 | def show 8 | @columns = @record.class.table_columns 9 | @headers = @record.class.table_headers 10 | end 11 | 12 | def destroy 13 | @draft.update(denied_by: current_user) 14 | redirect_after_update("Update was denied.") 15 | end 16 | 17 | def accept 18 | info = @draft.info 19 | @record.assign_attributes(info) 20 | 21 | if(@record.save) 22 | @draft.update(record: @record, accepted_by: current_user) 23 | redirect_after_update("#{@record.class.legacy_table_display_name.singularize} updated") 24 | else 25 | flash[:notice] = "Something went wrong." 26 | render :edit 27 | end 28 | end 29 | 30 | private 31 | 32 | def set_record 33 | @draft = Draft.find(params[:id]) 34 | @organization = @draft.info['organization'] 35 | @legacy_table_name = @draft.info['legacy_table_name'] 36 | if(@draft.record) 37 | @record = Location::Whitelist.find(@organization, @legacy_table_name).find(@draft.record_id) 38 | else 39 | @record = Location::Whitelist.find(@organization, @legacy_table_name).new 40 | end 41 | end 42 | 43 | def next_draft 44 | @next_draft ||= Draft. 45 | where("info->>'organization' = ?", @organization). 46 | where("info->>'legacy_table_name' = ?", @legacy_table_name). 47 | where(accepted_by_id: nil). 48 | where(denied_by_id: nil). 49 | first 50 | end 51 | 52 | def redirect_after_update(msg) 53 | if(next_draft.present?) 54 | path = location_draft_path(organization: @organization, legacy_table_name: @legacy_table_name, id: @next_draft.id) 55 | msg = msg+" Redirecting to next draft in queue." 56 | else 57 | path = locations_path(organization: @organization, legacy_table_name: @legacy_table_name) 58 | msg = msg+" All drafts processed. Keep up the good work and thank you." 59 | end 60 | 61 | redirect_to path, notice: msg 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /app/controllers/needs_controller.rb: -------------------------------------------------------------------------------- 1 | class NeedsController < ApplicationController 2 | before_action :set_headers 3 | before_action :set_need, only: [:show, :edit, :update, :destroy, :archive] 4 | 5 | def index 6 | @needs = Need.all 7 | end 8 | 9 | def new 10 | @need = Need.new 11 | end 12 | 13 | def create 14 | 15 | if(user_signed_in? && current_user.admin?) 16 | @need = Need.new(need_update_params) 17 | 18 | if @need.save 19 | redirect_to @need, notice: 'Need was successfully created.' 20 | else 21 | render :new 22 | end 23 | else 24 | draft = Draft.new(info: need_update_params.merge({record_type: Need.name})) 25 | 26 | if draft.save 27 | redirect_to draft, notice: 'Your new need is pending approval.' 28 | else 29 | @need = Need.new(need_update_params) 30 | render :new 31 | end 32 | end 33 | end 34 | 35 | def show 36 | @need = Need.find(params[:id]) 37 | end 38 | 39 | def destroy 40 | end 41 | 42 | def archive 43 | if(user_signed_in? && current_user.admin?) 44 | @need.update_attributes(active: false) 45 | redirect_to needs_path, notice: "Archived!" 46 | else 47 | redirect_to needs_path, notice: "You must be an admin to archive." 48 | end 49 | end 50 | 51 | def edit 52 | end 53 | 54 | def update 55 | if(user_signed_in? && current_user.admin?) 56 | if @need.update(need_update_params) 57 | redirect_to @need, notice: 'Need was successfully updated.' 58 | else 59 | render :edit 60 | end 61 | else 62 | draft = Draft.new(record: @need, info: need_update_params) 63 | 64 | if draft.save 65 | redirect_to draft, notice: 'Your need update is pending approval.' 66 | else 67 | render :edit 68 | end 69 | end 70 | end 71 | 72 | 73 | def drafts 74 | @drafts = Draft.includes(:record).where("record_type = ? OR info->>'record_type' = 'Need'", Need.name).where(accepted_by_id: nil).where(denied_by_id: nil) 75 | end 76 | 77 | def set_headers 78 | @columns = Need::ColumnNames 79 | @headers = Need::HeaderNames 80 | end 81 | 82 | def set_need 83 | @need = Need.find(params[:id]) 84 | end 85 | 86 | def need_update_params 87 | params.require(:need).permit(Need::UpdateFields).keep_if { |_,v| v.present? } 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /app/controllers/organizations_controller.rb: -------------------------------------------------------------------------------- 1 | class OrganizationsController < ApplicationController 2 | 3 | layout "locations" 4 | 5 | def show 6 | @organization = params[:organization] 7 | @organization_tables = Location::Whitelist.organization_tables(@organization) 8 | if !@organization_tables.present? 9 | redirect_to root_path, notice: "Unable to find organization." 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | 3 | def index 4 | if(user_signed_in? && current_user.admin?) 5 | @pages = Page.all 6 | else 7 | redirect_to root_path 8 | end 9 | end 10 | 11 | def edit 12 | if(user_signed_in? && current_user.admin?) 13 | @page = Page.find(params[:id]) 14 | else 15 | redirect_to root_path 16 | end 17 | end 18 | 19 | def update 20 | if(user_signed_in? && current_user.admin?) 21 | @page = Page.find(params[:id]) 22 | if @page.update(page_params) 23 | redirect_to root_path, notice: 'Page was successfully updated.' 24 | end 25 | else 26 | redirect_to root_path 27 | end 28 | end 29 | 30 | private 31 | 32 | def page_params 33 | params.require(:page).permit(:key, :content) 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /app/controllers/shelters_controller.rb: -------------------------------------------------------------------------------- 1 | class SheltersController < ApplicationController 2 | before_action :set_headers 3 | before_action :set_shelter, only: [:show, :edit, :update, :destroy, :archive] 4 | 5 | def index 6 | @shelters = Shelter.all 7 | @page = Page.shelters.first_or_initialize 8 | 9 | end 10 | 11 | def new 12 | @shelter = Shelter.new 13 | end 14 | 15 | def create 16 | if(admin?) 17 | @shelter = Shelter.new(shelter_update_params) 18 | 19 | if @shelter.save 20 | redirect_to @shelter, notice: 'Shelter was successfully created.' 21 | else 22 | render :new 23 | end 24 | else 25 | draft = Draft.new(info: shelter_update_params.merge({record_type: Shelter.name})) 26 | 27 | if draft.save 28 | redirect_to draft, notice: 'Your new shelter is pending approval.' 29 | else 30 | @shelter = Shelter.new(shelter_update_params) 31 | render :new 32 | end 33 | end 34 | end 35 | 36 | def show 37 | end 38 | 39 | def edit 40 | end 41 | 42 | def update 43 | if(admin?) 44 | if @shelter.update(shelter_update_params) 45 | redirect_to @shelter, notice: 'Shelter was successfully updated.' 46 | else 47 | render :edit 48 | end 49 | else 50 | draft = Draft.new(record: @shelter, info: shelter_update_params) 51 | 52 | if draft.save 53 | redirect_to draft, notice: 'Your shelter update is pending approval.' 54 | else 55 | render :edit 56 | end 57 | end 58 | end 59 | 60 | def destroy 61 | end 62 | 63 | def archive 64 | if(admin?) 65 | @shelter.update_attributes(active: false) 66 | redirect_to shelters_path, notice: "Archived!" 67 | else 68 | redirect_to shelters_path, notice: "You must be an admin to archive." 69 | end 70 | end 71 | 72 | def drafts 73 | @drafts = Draft.includes(:record).where("record_type = ? OR info->>'record_type' = 'Shelter'", Shelter.name).where(accepted_by_id: nil).where(denied_by_id: nil) 74 | end 75 | 76 | private 77 | 78 | # This is the definition of a beautiful hack. 1 part gross, 2 parts simplicity. Does something neat not clever. 79 | def set_headers 80 | if(admin?) 81 | @columns = Shelter::ColumnNames + Shelter::PrivateFields 82 | @headers = Shelter::HeaderNames + Shelter::PrivateFields.map(&:titleize) 83 | else 84 | @columns = Shelter::ColumnNames 85 | @headers = Shelter::HeaderNames 86 | end 87 | end 88 | 89 | def set_shelter 90 | @shelter = Shelter.find(params[:id]) 91 | end 92 | 93 | # TODO: Test private fields are only updatable by admin 94 | def shelter_update_params 95 | if(admin?) 96 | params.require(:shelter).permit(Shelter::UpdateFields + Shelter::PrivateFields).keep_if { |_,v| v.present? } 97 | else 98 | params.require(:shelter).permit(Shelter::UpdateFields).keep_if { |_,v| v.present? } 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /app/controllers/splash_controller.rb: -------------------------------------------------------------------------------- 1 | class SplashController < ApplicationController 2 | def index 3 | # They might not have created a home key yet 4 | @page = Page.home.first_or_initialize 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :authenticate_admin! 3 | 4 | def index 5 | @users = User.all 6 | end 7 | 8 | def show 9 | @user = User.find(params[:id]) 10 | end 11 | 12 | def update 13 | @user = User.find(params[:id]) 14 | @user.update(admin: params[:admin]) 15 | 16 | redirect_to @user, notice: "User is now #{@user.admin? ? "" : "not"} an admin." 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/connect/haves_helper.rb: -------------------------------------------------------------------------------- 1 | module Connect::HavesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/connect/needs_helper.rb: -------------------------------------------------------------------------------- 1 | module Connect::NeedsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/mucked_homes_helper.rb: -------------------------------------------------------------------------------- 1 | module MuckedHomesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/pages_helper.rb: -------------------------------------------------------------------------------- 1 | module PagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/splash_helper.rb: -------------------------------------------------------------------------------- 1 | module SplashHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/volunteers_helper.rb: -------------------------------------------------------------------------------- 1 | module VolunteersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/importers/api_importer.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'httparty' 3 | 4 | class APIImporter 5 | include HTTParty 6 | base_uri "https://api.harveyneeds.org/api/v1" 7 | format :json 8 | 9 | def self.needs 10 | get('/needs')["needs"] 11 | end 12 | 13 | def self.shelters 14 | get('/shelters')["shelters"] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/fetch_amazon_product_job.rb: -------------------------------------------------------------------------------- 1 | require 'vacuum' 2 | 3 | class FetchAmazonProductJob < ApplicationJob 4 | queue_as :default 5 | 6 | # The amazon product advertising API rate limits us pretty hard 7 | # So we need the connection_pool and redis to count our req/s 8 | # And the trafic control will re-schedule jobs 9 | throttle threshold: 10, period: 20.seconds unless Rails.env.test? 10 | 11 | def perform need 12 | 13 | request = Vacuum.new 14 | request.configure( 15 | aws_access_key_id: ENV.fetch("AWS_ACCESS_KEY_ID", "AKIAJ5PESCDQX7KIMQ5Q"), 16 | aws_secret_access_key: ENV.fetch("AWS_SECRET_ACCESS_KEY", ""), 17 | associate_tag: ENV.fetch("AWS_ASSOCIATE_TAG", "oneclickrelie-20") 18 | ) 19 | 20 | Rails.logger.debug "Finding for #{need}" 21 | 22 | response = request.item_search( 23 | query: { 24 | 'Keywords' => need, 25 | 'Availability' => 'Available', 26 | 'MerchantId' => 'Amazon', 27 | 'SearchIndex' => 'All', 28 | 'MaximumPrice' => 3000 29 | } 30 | ) 31 | 32 | error_code = response.dig("ItemSearchResponse", 33 | "Items", 34 | "Request", 35 | "Errors", 36 | "Error", 37 | "Code") 38 | 39 | if error_code == "AWS.ECommerceService.NoExactMatches" 40 | IgnoredAmazonProductNeed.create need: need 41 | return 42 | end 43 | 44 | item = response.dig('ItemSearchResponse', 'Items', 'Item') 45 | item = item.first if item.is_a? Array 46 | 47 | amazon_product = AmazonProduct 48 | .where("need ILIKE ?", "%#{need}%") 49 | .first_or_initialize 50 | amazon_product.need = need 51 | amazon_product.amazon_title = item["ItemAttributes"]["Title"] 52 | amazon_product.asin = item["ASIN"] 53 | amazon_product.detail_url = item["DetailPageURL"] 54 | 55 | amazon_product.save! 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /app/jobs/import_needs_job.rb: -------------------------------------------------------------------------------- 1 | class ImportNeedsJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform(*args) 5 | logger.info "Starting ImportNeedsJob #{Time.now}" 6 | needs = APIImporter.needs 7 | needs.each do |need| 8 | # needs and cleanPhone are derived fields 9 | # updatedAt is set on save to the database 10 | Need.create! need.except("needs", "cleanPhone", "updatedAt") 11 | end 12 | logger.info "ImportNeedsJob Complete - {#{needs.count}}" 13 | 14 | # schedule an update, but it's throttled to 1 every 10 minutes 15 | ScheduleAmazonFetchJob.perform_later 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /app/jobs/import_shelters_job.rb: -------------------------------------------------------------------------------- 1 | class ImportSheltersJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform(*args) 5 | logger.info "Starting ImportSheltersJob #{Time.now}" 6 | shelters = APIImporter.shelters 7 | shelters.each do |shelter| 8 | # needs and cleanPhone are derived fields 9 | # updatedAt is set on save to the database 10 | Shelter.create! shelter.except("needs", "cleanPhone", "updatedAt") 11 | end 12 | logger.info "ImportSheltersJob Complete - {#{shelters.count}}" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/jobs/need_update_notifier_job.rb: -------------------------------------------------------------------------------- 1 | class NeedUpdateNotifierJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform need 5 | NeedChannel.broadcast_to "needs", need: render(need) 6 | end 7 | 8 | def render need 9 | ApplicationController.render partial: 'api/v1/needs/need', 10 | locals: { 11 | need: need 12 | } 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/jobs/schedule_amazon_fetch_job.rb: -------------------------------------------------------------------------------- 1 | class ScheduleAmazonFetchJob < ApplicationJob 2 | queue_as :default 3 | 4 | # We can get scheduled constantly, but only run once every 10 minutes 5 | # Drop otherwise 6 | throttle threshold: 1, period: 10.minutes, drop: true unless Rails.env.test? 7 | 8 | def perform(*args) 9 | Rails.logger.debug "Starting ScheduleAmazonFetchJob" 10 | 11 | Need 12 | .all 13 | .map(&:clean_needs) 14 | .flatten 15 | .uniq 16 | .reject {|need| AmazonProduct.where("need ILIKE ?", "%#{need}%").exists? } 17 | .reject {|need| IgnoredAmazonProductNeed.where("need ILIKE ?", "%#{need}%").exists? } 18 | .tap {|needs| Rails.logger.debug "ScheduleAmazonFetchJob #{needs.count}"} 19 | .each { |need| FetchAmazonProductJob.perform_later(need) } 20 | 21 | Rails.logger.debug "ScheduleAmazonFetchJob Complete" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/jobs/shelter_update_notifier_job.rb: -------------------------------------------------------------------------------- 1 | class ShelterUpdateNotifierJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform shelter 5 | ShelterChannel.broadcast_to "shelters", shelter: render(shelter) 6 | end 7 | 8 | def render shelter 9 | ApplicationController.render partial: 'api/v1/shelters/shelter', 10 | locals: { 11 | shelter: shelter 12 | } 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/amazon_product.rb: -------------------------------------------------------------------------------- 1 | class AmazonProduct < ApplicationRecord 2 | 3 | ColumnNames = ["need", "asin", "amazon_title", "detail_url", "priority", "disabled"] 4 | 5 | HeaderNames = ColumnNames.map(&:titleize) 6 | 7 | UpdateFields = ["need", "asin", "amazon_title", "detail_url", "priority", "disabled"] 8 | 9 | 10 | validates :need, presence: true 11 | validates :asin, presence: true 12 | validates :detail_url, presence: true 13 | 14 | scope :active, -> { where(disabled: false).proper_categories.proper_pricing} 15 | scope :priority, -> { where(priority: true)} 16 | scope :proper_categories, -> {where("category_general != ? and category_specific !=?", "", "")} 17 | scope :proper_pricing, -> { where("price_in_cents > 0")} 18 | end 19 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/charitable_organization.rb: -------------------------------------------------------------------------------- 1 | class CharitableOrganization < ApplicationRecord 2 | ColumnNames = %w(name services food_bank donation_website phone_number email physical_address city state zip) 3 | 4 | HeaderNames = ColumnNames.map(&:titleize) 5 | 6 | UpdateFields = %w(name services food_bank donation_website phone_number email physical_address city state zip) 7 | 8 | default_scope { where(active: true) } 9 | 10 | has_many :drafts, as: :record 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/connect.rb: -------------------------------------------------------------------------------- 1 | module Connect 2 | def self.table_name_prefix 3 | 'connect_' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/models/connect/marker.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Connect 4 | class Marker < ApplicationRecord 5 | MARKER_TYPES = %w[have need].freeze 6 | 7 | validates :categories, :device_uuid, :name, :phone, presence: true 8 | validates :marker_type, inclusion: { in: MARKER_TYPES, allow_blank: false } 9 | validates :latitude, :longitude, numericality: { other_than: 0 } 10 | validates :email, format: { with: /\A.+@.+\z/, allow_nil: true } 11 | 12 | reverse_geocoded_by :latitude, :longitude 13 | after_validation :reverse_geocode, if: :coordinates_changed? 14 | 15 | scope :by_category, ->(category) { where('categories ? :category', category: category) } 16 | scope :by_device_uuid, ->(device_uuid) { where(device_uuid: device_uuid) } 17 | scope :by_type, ->(type) { where(marker_type: type) } 18 | scope :flagged, ->() { where("data ? 'inappropriate_flag'") } 19 | scope :not_flagged, ->() { where.not("data ? 'inappropriate_flag'") } 20 | scope :resolved, -> { where(resolved: true) } 21 | scope :unresolved, -> { where(resolved: false) } 22 | 23 | def coordinates_changed? 24 | return false if latitude.zero? || longitude.zero? 25 | latitude_changed? || longitude_changed? 26 | end 27 | 28 | def flagged_inappropriate? 29 | data.key? 'inappropriate_flag' 30 | end 31 | 32 | def flag_as_inappropriate!(device_uuid:, reason:) 33 | return if flagged_inappropriate? 34 | data['inappropriate_flag'] = { 35 | flagged_by: device_uuid, 36 | flagged_for: reason, 37 | flagged_at: Time.zone.now 38 | } 39 | save 40 | end 41 | 42 | def clear_inappropriate_flag! 43 | data.delete('inappropriate_flag') 44 | save! 45 | end 46 | 47 | def flagged_by 48 | data.dig 'inappropriate_flag', 'flagged_by' 49 | end 50 | 51 | def flagged_for 52 | data.dig 'inappropriate_flag', 'flagged_for' 53 | end 54 | 55 | def flagged_at 56 | data.dig 'inappropriate_flag', 'flagged_at' 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /app/models/draft.rb: -------------------------------------------------------------------------------- 1 | class Draft < ApplicationRecord 2 | belongs_to :record, polymorphic: true, optional: true 3 | belongs_to :accepted_by, class_name: 'User', optional: true 4 | belongs_to :denied_by, class_name: 'User', optional: true 5 | end 6 | -------------------------------------------------------------------------------- /app/models/ignored_amazon_product_need.rb: -------------------------------------------------------------------------------- 1 | class IgnoredAmazonProductNeed < ApplicationRecord 2 | end 3 | -------------------------------------------------------------------------------- /app/models/location/hhrd/pois.rb: -------------------------------------------------------------------------------- 1 | class Location 2 | module HHRD 3 | class Pois < Location 4 | 5 | # Import, Models, HTML Controller, API Controller 6 | config( 7 | legacy_table_name: "point-of-interest", 8 | organization: "hurricane-harvey-rescue-dispatchers" 9 | ) 10 | 11 | filter(:rally_point, type: :truthy) 12 | filter(:launch_point, type: :truthy) 13 | filter(:diesel_fuel, type: :truthy) 14 | filter(:unleadded_fuel, type: :truthy) 15 | filter(:food, type: :truthy) 16 | filter(:hours) 17 | filter(:notes) 18 | 19 | legacy_field(:rally_point, type: :boolean) 20 | legacy_field(:launch_point, type: :boolean) 21 | legacy_field(:diesel_fuel, type: :boolean) 22 | legacy_field(:unleaded_fuel, type: :boolean) 23 | legacy_field(:food, type: :boolean) 24 | legacy_field(:hours) 25 | legacy_field(:notes) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/location/hhrd/rescuees.rb: -------------------------------------------------------------------------------- 1 | class Location 2 | module HHRD 3 | class Rescuees < Location 4 | 5 | # Import, Models, HTML Controller, API Controller 6 | config( 7 | legacy_table_name: "rescuees", 8 | organization: "hurricane-harvey-rescue-dispatchers" 9 | ) 10 | 11 | filter(:status) 12 | filter(:tier) 13 | filter(:high_water_vehicle_accessable, type: :truthy) 14 | filter(:number_of_people) 15 | filter(:notes) 16 | 17 | legacy_field(:apartment_number) 18 | legacy_field(:status, type: :enum, options: ["Awaiting Rescue", "Boat in Route", "Rescued"]) 19 | legacy_field(:tier, type: :enum, options: ["Tier 1", "Tier 2", "None"]) 20 | legacy_field(:high_water_vehicle_accessible, type: :boolean) 21 | legacy_field(:number_of_adults) 22 | legacy_field(:number_of_elderly) 23 | legacy_field(:number_of_children) 24 | legacy_field(:number_of_pets) 25 | legacy_field(:notes) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/location/hhrd/rescuers.rb: -------------------------------------------------------------------------------- 1 | class Location 2 | module HHRD 3 | class Rescuers < Location 4 | 5 | # Import, Models, HTML Controller, API Controller 6 | config( 7 | legacy_table_name: "rescuers", 8 | organization: "hurricane-harvey-rescue-dispatchers" 9 | ) 10 | 11 | filter(:status) 12 | filter(:background_check, type: :truthy) 13 | filter(:medic, type: :truthy) 14 | filter(:mechanic, type: :truthy) 15 | filter(:mechanic, type: :truthy) 16 | filter(:has_parts, type: :truthy) 17 | filter(:high_water_vehicle, type: :truthy) 18 | 19 | legacy_field(:status, type: :enum, options: ['in route', 'returning', 'available', 'partially occupied']) 20 | legacy_field(:vehicle_type, type: :enum, options: ['high water vehicle', 'boat']) 21 | legacy_field(:capacity) 22 | legacy_field(:background_check, type: :boolean) 23 | legacy_field(:medic, type: :boolean) 24 | legacy_field(:mechanic, type: :boolean) 25 | legacy_field(:has_parts, type: :boolean) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/location/whitelist.rb: -------------------------------------------------------------------------------- 1 | class Location 2 | module Whitelist 3 | Locations = [HHRD::Rescuers, HHRD::Rescuees, HHRD::Pois] 4 | 5 | LocationsMap = Locations.each_with_object({}.with_indifferent_access) do |location, obj| 6 | organization = location.organization 7 | legacy_table_name = location.legacy_table_name 8 | obj[organization] ||= {}.with_indifferent_access 9 | obj[organization][legacy_table_name] ||= location 10 | end 11 | 12 | def self.find(organization, legacy_table_name) 13 | LocationsMap[organization] && LocationsMap[organization][legacy_table_name] 14 | end 15 | 16 | def self.organization_tables(organization) 17 | return nil if LocationsMap[organization].nil? 18 | 19 | keys = LocationsMap[organization].keys.sort 20 | keys.map { |key| LocationsMap[organization][key] } 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/models/mucked_home.rb: -------------------------------------------------------------------------------- 1 | class MuckedHome < ApplicationRecord 2 | geocoded_by :full_address 3 | end 4 | -------------------------------------------------------------------------------- /app/models/need.rb: -------------------------------------------------------------------------------- 1 | class Need < ApplicationRecord 2 | ColumnNames = ["id", "location_name", "location_address", "contact_for_this_location_name", "contact_for_this_location_phone_number", "are_supplies_needed", "tell_us_about_the_supply_needs", "are_volunteers_needed", "tell_us_about_the_volunteer_needs", "anything_else_you_would_like_to_tell_us", "source", "created_at", "updated_at", "updated_by", "latitude", "longitude"] 3 | 4 | HeaderNames = ColumnNames.map(&:titleize) 5 | 6 | UpdateFields = ["anything_else_you_would_like_to_tell_us", "are_supplies_needed", "are_volunteers_needed", "contact_for_this_location_name", "contact_for_this_location_phone_number", "created_at", "location_address", "location_name", "source", "tell_us_about_the_supply_needs", "tell_us_about_the_volunteer_needs", "updated_at", "updated_by"] 7 | 8 | has_many :drafts, as: :record 9 | 10 | geocoded_by :location_address 11 | default_scope { where(active: !false) } 12 | 13 | def clean_needs 14 | return [] if tell_us_about_the_supply_needs.blank? 15 | tell_us_about_the_supply_needs 16 | .gsub("\n","") 17 | .gsub("*", ",") 18 | .split(",") 19 | .reject{|n| n =~ /^open/i } 20 | .map(&:strip) 21 | .select(&:present?) 22 | end 23 | 24 | after_commit do 25 | NeedUpdateNotifierJob.perform_later self 26 | end 27 | 28 | before_save :calculate_values 29 | 30 | def calculate_values 31 | self.calculated_needs = (tell_us_about_the_volunteer_needs ||"").split(",") + (tell_us_about_the_supply_needs || "").split(",") 32 | self.calculated_updated_at_rfc3339 = (updated_at || Time.now).in_time_zone("Central Time (US & Canada)").rfc3339 33 | stripped_phone = (contact_for_this_location_phone_number||"").gsub(/\D/,"") 34 | self.calculated_phone = stripped_phone.match?(/^\d{10}$/) ? stripped_phone : "badphone" 35 | end 36 | 37 | def has_sufficient_data_for_map? 38 | latitude.present? and longitude.present? 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/models/page.rb: -------------------------------------------------------------------------------- 1 | class Page < ApplicationRecord 2 | validates :key, presence: true, uniqueness: true 3 | validates :content, presence: true 4 | 5 | scope :home, -> { where(key: "home")} 6 | scope :shelters, -> { where(key: "shelters")} 7 | end 8 | -------------------------------------------------------------------------------- /app/models/shelter.rb: -------------------------------------------------------------------------------- 1 | class Shelter < ApplicationRecord 2 | ColumnNames = ["id", "accepting", "address", "address_name", "city", "county", 3 | "state", "notes", "pets", "phone", "shelter", "source", 4 | "supply_needs", "updated_by", "volunteer_needs", 5 | "distribution_center", "food_pantry", "updated_at", 6 | "latitude", "longitude"] 7 | 8 | HeaderNames = ColumnNames.map(&:titleize) 9 | 10 | UpdateFields = ["accepting", "address", "address_name", "city", "county", 11 | "state", "notes", "pets", "phone", "shelter", "source", 12 | "supply_needs", "updated_by", "volunteer_needs", 13 | "distribution_center", "food_pantry", "latitude", "longitude"] 14 | 15 | PrivateFields = ["private_notes"] 16 | 17 | has_many :drafts, as: :record 18 | default_scope { where(active: !false) } 19 | 20 | geocoded_by :address 21 | 22 | after_commit do 23 | ShelterUpdateNotifierJob.perform_later self 24 | end 25 | 26 | before_save :calculate_values 27 | 28 | def calculate_values 29 | self.calculated_needs = (volunteer_needs || "").split(",") + (supply_needs || "").split(",") 30 | self.calculated_updated_at_rfc3339 = (updated_at || Time.now).in_time_zone("Central Time (US & Canada)").rfc3339 31 | stripped_phone = (phone||"").gsub(/\D/,"") 32 | self.calculated_phone = stripped_phone.match?(/^\d{10}$/) ? stripped_phone : "badphone" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | # Include default devise modules. Others available are: 3 | # :recoverable, :confirmable, :lockable, :timeoutable and :omniauthable 4 | devise :database_authenticatable, :registerable, 5 | :rememberable, :trackable, :validatable 6 | end 7 | -------------------------------------------------------------------------------- /app/models/volunteer.rb: -------------------------------------------------------------------------------- 1 | class Volunteer < ApplicationRecord 2 | geocoded_by :full_address 3 | end 4 | -------------------------------------------------------------------------------- /app/views/amazon_products/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: amazon_product, local: true) do |f| %> 2 | <% if amazon_product.errors.any? %> 3 |
4 |

<%= pluralize(amazon_product.errors.count, "error") %> prohibited this amazon_product from being saved:

5 | 6 | 11 |
12 | <% end %> 13 | 14 | 15 |
16 | <%= f.label :need %> 17 | <%= f.text_field :need, disabled: true %> 18 | 19 | <%= f.label :asin %> 20 | <%= f.text_field :asin %> 21 | 22 | <%= f.label :amazon_title %> 23 | <%= f.text_field :amazon_title %> 24 | 25 | <%= f.label :detail_url%> 26 | <%= f.text_field :detail_url %> 27 | 28 | <%= f.label :priority do %> 29 | <%= f.check_box :priority %> Priority 30 | <% end %> 31 | 32 | <%= f.label :disabled do %> 33 | <%= f.check_box :disabled %> Disabled 34 | <% end %> 35 | 36 |
<%= f.button :submit %>
37 |
38 | <% end %> 39 | -------------------------------------------------------------------------------- /app/views/amazon_products/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= @amazon_product.need_was %>

2 | 3 | <%= render 'form', amazon_product: @amazon_product %> 4 | -------------------------------------------------------------------------------- /app/views/amazon_products/index.html.erb: -------------------------------------------------------------------------------- 1 |

Amazon Products

2 | 3 | <%= render "shared/table", rows: @amazon_products, headers: @headers, columns: @columns %> 4 | -------------------------------------------------------------------------------- /app/views/amazon_products/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @amazon_product.need %>

2 | 3 | <%= render "shared/show", record: @amazon_product, headers: @headers, columns: @columns %> 4 | 5 | <%= link_to 'Update', [:edit, @amazon_product], class: "button button-clear" %> | 6 | <%= link_to 'Back', amazon_products_path, class: "button button-clear" %> 7 | -------------------------------------------------------------------------------- /app/views/api/v1/amazon_products/_amazon_product.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! amazon_product, :id, :need, :asin, :amazon_title, :detail_url, 2 | :priority, :category_specific, :category_general, 3 | :price_in_cents 4 | json.price number_to_currency((amazon_product.price_in_cents/100.0)) 5 | -------------------------------------------------------------------------------- /app/views/api/v1/amazon_products/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.cache! @products do 2 | json.products @products do |product| 3 | json.cache! product do 4 | json.partial! product 5 | end 6 | end 7 | end 8 | 9 | json.meta do 10 | json.filters @filters 11 | json.result_count @products.count 12 | end 13 | -------------------------------------------------------------------------------- /app/views/api/v1/charitable_organizations/_charitable_organization.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! charitable_organization, :id, :name, :services, :food_bank, 2 | :donation_website, :phone_number, :email, :physical_address, :city, :state, :zip 3 | 4 | 5 | begin 6 | json.updatedAt charitable_organization.updated_at 7 | rescue 8 | json.updatedAt "baddate" 9 | end 10 | -------------------------------------------------------------------------------- /app/views/api/v1/charitable_organizations/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.cache! @charitable_organizations do 2 | json.charitable_organizations @charitable_organizations do |charitable_organization| 3 | json.cache! charitable_organization do 4 | json.partial! charitable_organization 5 | end 6 | end 7 | end 8 | 9 | json.meta do 10 | json.result_count @charitable_organizations.length 11 | json.filters @filters 12 | end 13 | -------------------------------------------------------------------------------- /app/views/api/v1/connect/markers/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.markers @markers do |marker| 2 | 3 | json.extract! marker, :id, :marker_type, :name, :description, :phone, 4 | :categories, :latitude, :longitude, :address, :email, :data 5 | 6 | json.updated_at marker.updated_at.in_time_zone("Central Time (US & Canada)").rfc3339 7 | end 8 | 9 | json.meta do 10 | json.result_count @markers.length 11 | json.filters @filters 12 | end 13 | -------------------------------------------------------------------------------- /app/views/api/v1/connect/markers/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! @marker, :id, :marker_type, :name, :description, :phone, :categories, 2 | :resolved, :latitude, :longitude, :address, :email, :data 3 | 4 | json.updated_at @marker.updated_at.in_time_zone("Central Time (US & Canada)").rfc3339 5 | -------------------------------------------------------------------------------- /app/views/api/v1/needs/_need.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! need, :id, :updated_by, :location_name, 2 | :location_address , :longitude , :latitude , :contact_for_this_location_name, 3 | :contact_for_this_location_phone_number , :are_volunteers_needed, 4 | :tell_us_about_the_volunteer_needs , :are_supplies_needed, 5 | :tell_us_about_the_supply_needs , :anything_else_you_would_like_to_tell_us 6 | :source 7 | 8 | json.needs need.calculated_needs 9 | 10 | json.updated_at need.calculated_updated_at_rfc3339 11 | json.updatedAt need.calculated_updated_at_rfc3339 12 | json.last_updated need.calculated_updated_at_rfc3339 13 | json.cleanPhone need.calculated_phone 14 | -------------------------------------------------------------------------------- /app/views/api/v1/needs/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.cache! @needs do 2 | json.needs @needs do |need| 3 | json.cache! need do 4 | json.partial! need 5 | end 6 | end 7 | end 8 | 9 | json.meta do 10 | json.result_count @needs.length 11 | json.filters @filters 12 | end 13 | -------------------------------------------------------------------------------- /app/views/api/v1/shelters/_shelter.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! shelter, :id, :county, :shelter, :address, :city, :pets, 2 | :phone, :accepting, :updated_by, :notes, 3 | :volunteer_needs, :longitude, :latitude, :supply_needs, :source 4 | 5 | json.needs shelter.calculated_needs 6 | 7 | json.updated_at shelter.calculated_updated_at_rfc3339 8 | json.updatedAt shelter.calculated_updated_at_rfc3339 9 | json.last_updated shelter.calculated_updated_at_rfc3339 10 | json.cleanPhone shelter.calculated_phone 11 | -------------------------------------------------------------------------------- /app/views/api/v1/shelters/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.cache! @shelters do 2 | json.shelters @shelters do |shelter| 3 | json.cache! shelter do 4 | json.partial! shelter 5 | end 6 | end 7 | end 8 | 9 | json.meta do 10 | json.result_count @shelters.length 11 | json.filters @filters 12 | end 13 | -------------------------------------------------------------------------------- /app/views/api/v2/locations/_location.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! location, *@location_class.table_columns 2 | 3 | json.show_url location_url(organization: location.organization, legacy_table_name: location.legacy_table_name, id: location.id) 4 | json.edit_url edit_location_url(organization: location.organization, legacy_table_name: location.legacy_table_name, id: location.id) 5 | json.updatedAt location.updated_at 6 | json.latitude location.latitude 7 | json.longitude location.longitude 8 | -------------------------------------------------------------------------------- /app/views/api/v2/locations/help.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.filters @location_class.filters.values do |filter| 2 | json.name filter.name 3 | json.type filter.type 4 | json.description filter.description 5 | end 6 | 7 | json.fields @location_class.api_help do |field| 8 | json.name field.name 9 | json.type field.type 10 | json.required field.required 11 | json.options field.options do |option| 12 | json.option option 13 | end 14 | json.description field.description 15 | end 16 | -------------------------------------------------------------------------------- /app/views/api/v2/locations/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.cache! @locations do 2 | json.locations @locations do |location| 3 | json.cache! location do 4 | json.partial! "location", location: location 5 | end 6 | end 7 | end 8 | 9 | json.meta do 10 | json.result_count @locations.length 11 | json.filters @filters 12 | end 13 | -------------------------------------------------------------------------------- /app/views/api/v2/locations/routes.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.routes Location::Whitelist::Locations do |location| 2 | json.extract! location, :organization, :legacy_table_name 3 | json.api_url api_v2_api_locations_url(organization: location.organization, legacy_table_name: location.legacy_table_name) 4 | json.api_help_url api_v2_api_locations_help_url(organization: location.organization, legacy_table_name: location.legacy_table_name) 5 | end 6 | -------------------------------------------------------------------------------- /app/views/charitable_organizations/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for(@charitable_organization, local: true) do |f| %> 2 | <% if charitable_organization.errors.any? %> 3 |
4 |

<%= pluralize(charitable_organization.errors.count, "error") %> prohibited this charitable organization from being saved:

5 | 6 | 11 |
12 | <% end %> 13 | 14 |
15 | <%= f.input :name %> 16 | <%= f.input :services %> 17 | <%= f.input :food_bank %> 18 | <%= f.input :donation_website %> 19 | <%= f.input :phone_number %> 20 | <%= f.input :email %> 21 | <%= f.input :physical_address %> 22 | <%= f.input :city %> 23 | <%= f.input :state %> 24 | <%= f.input :zip %> 25 |
26 | 27 |
28 | <%= f.button :submit %> 29 |
30 | <% end %> 31 | -------------------------------------------------------------------------------- /app/views/charitable_organizations/drafts.html.erb: -------------------------------------------------------------------------------- 1 |

Charitable Organizations Update Queue

2 | 3 | <%= render "shared/drafts_table", rows: @drafts, columns: @columns, headers: @headers %> 4 | -------------------------------------------------------------------------------- /app/views/charitable_organizations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= @charitable_organization.name %>

2 | 3 | <%= render 'form', charitable_organization: @charitable_organization %> 4 | -------------------------------------------------------------------------------- /app/views/charitable_organizations/index.html.erb: -------------------------------------------------------------------------------- 1 |

Charitable Organizations

2 | 3 | <%= link_to 'Add New Charitable Organization', new_charitable_organization_path, class: "button button-outline" %> 4 | 5 | <%= render "shared/table", rows: @charitable_organizations, headers: @headers, columns: @columns %> 6 | -------------------------------------------------------------------------------- /app/views/charitable_organizations/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Charitable Organization

2 | 3 | <%= render 'form', charitable_organization: @charitable_organization %> 4 | -------------------------------------------------------------------------------- /app/views/charitable_organizations/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @charitable_organization.name %>

2 | 3 | <%= render "shared/show", record: @charitable_organization, headers: @headers, columns: @columns %> 4 | 5 | <%= link_to 'Update', [:edit, @charitable_organization], class: "button button-clear" %> | 6 | <%= link_to 'Back', charitable_organizations_path, class: "button button-clear" %> 7 | <% if user_signed_in? && current_user.admin? %> | 8 | <%= link_to 'Archive', archive_charitable_organization_path(@charitable_organization), 9 | method: :post, 10 | data: { confirm: 'Are you sure?' }, 11 | class: "button button-clear" %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /app/views/connect/flagged_markers/index.html.erb: -------------------------------------------------------------------------------- 1 |

Flagged Markers

2 | 3 |

4 | These are map markers from the Disaster Connect app 5 | that users have flagged as inappropriate. Markers that have been flagged will be hidden from 6 | public view in the apps. 7 |

8 |

9 | As an admin, you have the ability to clear the flag, which will enable the public to view the 10 | marker again. Or, you can confirm that the marker is inappropriate and it will remain hidden 11 | from public view and this screen as well. 12 |

13 | 14 | 15 | 16 | 17 | 18 | <% fields.map(&:titleize).each do |header| %> 19 | 20 | <% end %> 21 | 22 | 23 | 24 | 25 | <% @flagged_markers.each do |row| %> 26 | 27 | 28 | <% fields.each do |field| %> 29 | 30 | <% end %> 31 | 32 | <% end %> 33 | 34 |
<%= header %>
<%= link_to 'Show', connect_flagged_marker_path(row) %><%= truncate(row.public_send(field).to_s) %>
35 | 36 | 44 | -------------------------------------------------------------------------------- /app/views/connect/flagged_markers/show.html.erb: -------------------------------------------------------------------------------- 1 |

Marker <%= @flagged_marker.id %>

2 | 3 | <%= link_to 'Confirm', connect_flagged_marker_path(@flagged_marker), 4 | method: :patch, 5 | title: "Confirm that this marker is inappropriate", 6 | class: "button button-outline" %> | 7 | <%= link_to 'Clear', connect_flagged_marker_path(@flagged_marker), 8 | method: :delete, 9 | title: "This marker is fine and should not have been flagged", 10 | class: "button button-outline" %> | 11 | <%= link_to 'Back', connect_flagged_markers_path, class: "button button-outline" %> 12 | 13 |
14 |
15 |

Flagged At: <%= @flagged_marker.flagged_at %>

16 |

Flagged For: <%= @flagged_marker.flagged_for %>

17 |

Flagged By: <%= @flagged_marker.flagged_by %>

18 | 19 |

Marker Details

20 |
<%= JSON.pretty_generate @flagged_marker.as_json %>
21 |
22 |
23 |
24 | 34 |
35 |
36 | -------------------------------------------------------------------------------- /app/views/drafts/show.html.erb: -------------------------------------------------------------------------------- 1 | <% if @record.persisted? %> 2 | <% if @draft.accepted_by_id.nil? && @draft.denied_by_id.nil? %> 3 |

View Proposed Changes for <%= @record.class.name %>

4 |
Changes in grey rows.
5 | <% else %> 6 |

View Draft (already accepted or denied)

7 | <% end %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @columns.each_with_index do |column, index| %> 19 | <% next if ['id', 'updated_at'].include?(column) %> 20 | <%= content_tag :tr, class: (@draft.info[column].nil? || @draft.info[column] == @record.send(column)) ? "" : "striped" do %> 21 | 22 | 23 | 24 | <% end %> 25 | <% end %> 26 | 27 |
FieldCurrent ValueNew Value
<%= @headers[index] %><%= @record.send(column) %><%= @draft.info[column.to_s] || @record.send(column) %>
28 | <% else %> 29 | <% if @draft.accepted_by_id.nil? && @draft.denied_by_id.nil? %> 30 |

New Entry for <%= @record.class.name %>

31 | <% else %> 32 |

View Draft (already accepted or denied)

33 | <% end %> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | <% @columns.each_with_index do |column, index| %> 44 | <% next if ['id', 'updated_at'].include?(column) %> 45 | <%= content_tag :tr do %> 46 | 47 | 48 | <% end %> 49 | <% end %> 50 | 51 |
FieldValue
<%= @headers[index] %><%= @draft.info[column.to_s] %>
52 | <% end %> 53 | 54 | <% if user_signed_in? && current_user.admin? && @draft.accepted_by_id.nil? && @draft.denied_by_id.nil? %> 55 | <%= link_to 'Accept', [:accept, @draft], {class: "button", method: :post} %> 56 | <%= link_to 'Deny', @draft, {class: "button button-outline", method: :delete} %> 57 | <% end %> 58 | -------------------------------------------------------------------------------- /app/views/layouts/locations.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= @application_name %> 5 | <%= csrf_meta_tags %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 23 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 24 | 25 | 26 | 27 | 28 | 54 |
55 |

<%= notice %>

56 |

<%= alert %>

57 |
58 | <%= yield %> 59 |
60 | 61 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/location_drafts/show.html.erb: -------------------------------------------------------------------------------- 1 | <% if @record.persisted? %> 2 | <% if @draft.accepted_by_id.nil? && @draft.denied_by_id.nil? %> 3 |

View Proposed Changes for <%= @record.class.legacy_table_display_name.singularize %>

4 |
Changes in grey rows.
5 | <% else %> 6 |

View Draft (already accepted or denied)

7 | <% end %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @columns.each_with_index do |column, index| %> 19 | <% next if ['id', 'updated_at'].include?(column) %> 20 | <%= content_tag :tr, class: (@draft.info[column.to_s].nil? || @draft.info[column.to_s] == @record.send(column)) ? "" : "striped" do %> 21 | 22 | 23 | 24 | <% end %> 25 | <% end %> 26 | 27 |
FieldCurrent ValueNew Value
<%= @headers[index] %><%= @record.send(column) %><%= @draft.info[column.to_s] || @record.send(column) %>
28 | <% else %> 29 | <% if @draft.accepted_by_id.nil? && @draft.denied_by_id.nil? %> 30 |

New Entry for <%= @record.class.legacy_table_display_name.singularize %>

31 | <% else %> 32 |

View Draft (already accepted or denied)

33 | <% end %> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | <% @columns.each_with_index do |column, index| %> 44 | <% next if ['id', 'updated_at'].include?(column) %> 45 | <%= content_tag :tr do %> 46 | 47 | 48 | <% end %> 49 | <% end %> 50 | 51 |
FieldValue
<%= @headers[index] %><%= @draft.info[column.to_s] %>
52 | <% end %> 53 | 54 | <% if user_signed_in? && current_user.admin? && @draft.accepted_by_id.nil? && @draft.denied_by_id.nil? %> 55 | <%= link_to 'Accept', accept_location_draft_path(organization: @organization, legacy_table_name: @legacy_table_name, id: @draft.id), {class: "button", method: :post} %> 56 | <%= link_to 'Deny', location_draft_path(organization: @organization, legacy_table_name: @legacy_table_name, id: @draft.id), {class: "button button-outline", method: :delete} %> 57 | <% end %> 58 | -------------------------------------------------------------------------------- /app/views/locations/_form.html.erb: -------------------------------------------------------------------------------- 1 |
NOTE: You must fill out the name, address, city, state and zip or the system will be unable to add the lat/lon to record and it will not appear on the map!
2 | <%= form_for(location, url: path, method: method, local: true) do |f| %> 3 | <% if location.errors.any? %> 4 |
5 |

<%= pluralize(shelter.errors.count, "error") %> prohibited this shelter from being saved:

6 | 11 |
12 | <% end %> 13 | 14 | 15 |
16 | <% update_fields.each do |field| %> 17 | <%= f.label field.name, "#{field.name.to_s.titleize}#{field.required ? ' (required)' : ""}" %> 18 | <% case field.type %> 19 | <% when :boolean %> 20 | <%= f.check_box field.name, required:field.required %> 21 | <% when :enum %> 22 | <%= f.select field.name, options_for_select([nil].concat(field.options), location.send(field.name)), required: field.required %> 23 | <% when :phone %> 24 | <%= f.telephone_field field.name, required: field.required %> 25 | <% else %> 26 | <%= f.text_field field.name, required: field.required %> 27 | <% end %> 28 | <% end %> 29 | 30 | <%= f.button :submit %> 31 |
32 | <% end %> 33 | -------------------------------------------------------------------------------- /app/views/locations/drafts.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @legacy_table_display_name %> Update Queue

2 | 3 | 4 | 5 | 6 | 7 | <% @headers.each do |header| %> 8 | 9 | <% end %> 10 | 11 | 12 | 13 | 14 | <% @drafts.each do |draft| %> 15 | 16 | 17 | <% @columns.each do |column| %> 18 | 19 | <% end %> 20 | 21 | <% end %> 22 | 23 |
<%= header %>
<%= link_to 'Show', location_draft_path(organization: @organization, legacy_table_name: @legacy_table_name, id: draft.id) %><%= truncate((draft.info[column.to_s] || @locations[draft.record_id] && @locations[draft.record_id].send(column)).to_s) %>
24 | 25 | 32 | -------------------------------------------------------------------------------- /app/views/locations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= @legacy_table_display_name.singularize %>

2 | 3 | <%= render 'form', 4 | path: location_path(organization: @organization, legacy_table_name: @legacy_table_name, id: @location.id), 5 | method: 'PUT', 6 | location: @location, 7 | update_fields: @update_fields 8 | %> 9 | -------------------------------------------------------------------------------- /app/views/locations/index.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @legacy_table_display_name %>

2 | 3 | 4 | 27 | 28 | <%= link_to "Add New #{@legacy_table_display_name.singularize}", new_location_path(organization: @organization, legacy_table_name: @legacy_table_name), class: "button button-outline" %> 29 | 30 | 31 | 32 | 33 | 34 | 35 | <% @headers.each do |header| %> 36 | 37 | <% end %> 38 | 39 | 40 | 41 | 42 | <% @locations.each do |location| %> 43 | 44 | 45 | 46 | <% @columns.each do |column| %> 47 | 48 | <% end %> 49 | 50 | <% end %> 51 | 52 |
Geocoded?<%= header %>
<%= link_to 'Show', location_path(organization: @organization, legacy_table_name: @legacy_table_name, id: location.id) %> / <%= link_to 'Update', edit_location_path(organization: @organization, legacy_table_name: @legacy_table_name, id: location.id) %><%= location.latitude.present? && location.longitude.present? %><%= truncate(location.send(column).to_s) %>
53 | 54 | 62 | -------------------------------------------------------------------------------- /app/views/locations/new.html.erb: -------------------------------------------------------------------------------- 1 |

New <%= @legacy_table_display_name.singularize %>

2 | 3 | <%= render 'form', 4 | path: locations_path(organization: @organization, legacy_table_name: @legacy_table_name), 5 | method: 'POST', 6 | location: @location, 7 | update_fields: @update_fields 8 | %> 9 | -------------------------------------------------------------------------------- /app/views/locations/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @legacy_table_display_name.singularize %>

2 | 3 | <%= render "shared/show", record: @location, headers: @headers, columns: @columns %> 4 | 5 | <%= link_to 'Update', edit_location_path(organization: @organization, legacy_table_name: @legacy_table_name, id: @location.id), class: "button button-clear" %> | 6 | <%= link_to 'Back', locations_path(organization: @organization, legacy_table_name: @legacy_table_name), class: "button button-clear" %> 7 | <% if user_signed_in? && current_user.admin? %> | 8 | <%= link_to 'Archive', archive_location_path(organization: @organization, legacy_table_name: @legacy_table_name, id: @location.id), 9 | method: :post, 10 | data: { confirm: 'Are you sure?' }, 11 | class: "button button-clear" 12 | %> 13 | <% end %> 14 | -------------------------------------------------------------------------------- /app/views/needs/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: need, local: true) do |f| %> 2 | <% if need.errors.any? %> 3 |
4 |

<%= pluralize(need.errors.count, "error") %> prohibited this need from being saved:

5 | 6 | 11 |
12 | <% end %> 13 | 14 | 15 |
16 | 17 |
18 |
19 | <%= f.label :google_place, 'Where is it?' %> 20 | <%= f.text_field :google_place, :class => 'place-autocomplete', :data => { 21 | :name => '#geocode-name', 22 | :address => '#geocode-address', 23 | :phone => '#geocode-phone', 24 | :lat => '#geocode-lat', 25 | :lng => '#geocode-lng', 26 | :placeid => '#geocode-place-id', 27 | :city => '#geocode-city', 28 | :mapselector => '#map' 29 | }, :required => true, :autofocus => true %> 30 | 31 | <%= f.label :location_name %> 32 | <%= f.text_field :location_name, :required => true, :id => 'geocode-name' %> 33 | <%= f.hidden_field :latitude, :id => 'geocode-lat' %> 34 | <%= f.hidden_field :longitude, :id => 'geocode-lng' %> 35 | <%= f.hidden_field :location_address, :required => true, :id => 'geocode-address' %> 36 | 37 | <%= f.label :contact_for_this_location_name %> 38 | <%= f.text_field :contact_for_this_location_name %> 39 | 40 | <%= f.label :contact_for_this_location_phone_number %> 41 | <%= f.text_field :contact_for_this_location_phone_number, :required => true %> 42 | 43 |
44 |
45 | 46 |
47 | 48 | <% if need.latitude && need.longitude %> 49 | 59 | <% end %> 60 |
61 |
62 | 63 | <%= f.label :are_volunteers_needed %> 64 | <%= f.text_field :are_volunteers_needed %> 65 | 66 | <%= f.label :tell_us_about_the_volunteer_needs %> 67 | <%= f.text_area :tell_us_about_the_volunteer_needs %> 68 | 69 | <%= f.label :are_supplies_needed %> 70 | <%= f.text_area :are_supplies_needed %> 71 | 72 | <%= f.label :tell_us_about_the_supply_needs %> 73 | <%= f.text_area :tell_us_about_the_supply_needs %> 74 | 75 | <%= f.label :anything_else_you_would_like_to_tell_us %> 76 | <%= f.text_area :anything_else_you_would_like_to_tell_us %> 77 | 78 | <%= f.label :source %> 79 | <%= f.text_field :source %> 80 | 81 | <%= f.button :submit %> 82 |
83 | <% end %> 84 | -------------------------------------------------------------------------------- /app/views/needs/drafts.html.erb: -------------------------------------------------------------------------------- 1 |

Needs Update Queue

2 | 3 | <%= render "shared/drafts_table", rows: @drafts, columns: @columns, headers: @headers %> 4 | -------------------------------------------------------------------------------- /app/views/needs/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit Need <%= @need.id %>

2 | 3 | <%= render 'form', need: @need %> 4 | -------------------------------------------------------------------------------- /app/views/needs/index.html.erb: -------------------------------------------------------------------------------- 1 |

Needs

2 | 3 | 26 | 27 | <%= link_to 'Add New Need', new_need_path, class: "button button-outline" %> 28 | 29 | <%= render "shared/table", rows: @needs, headers: @headers, columns: @columns %> 30 | -------------------------------------------------------------------------------- /app/views/needs/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Need

2 | 3 | <%= render 'form', need: @need %> 4 | -------------------------------------------------------------------------------- /app/views/needs/show.html.erb: -------------------------------------------------------------------------------- 1 |

Need <%= @need.id %>

2 | 3 | <%= link_to 'Update', [:edit, @need], class: "button button-clear" %> | 4 | <%= link_to 'Back', needs_path, class: "button button-clear" %> 5 | <% if user_signed_in? && current_user.admin? %> | 6 | <%= link_to 'Archive', archive_need_path(@need), 7 | method: :post, 8 | data: { confirm: 'Are you sure?' }, 9 | class: "button button-clear" %> 10 | <% end %> 11 | 12 | 13 |
14 |
15 | <%= render "shared/show", record: @need, headers: @headers, columns: @columns %> 16 |
17 | <% if @need.has_sufficient_data_for_map? %> 18 |
19 |
20 | 30 |
31 | <% end %> 32 |
33 | -------------------------------------------------------------------------------- /app/views/organizations/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @organization.titleize %>

2 | 3 |

Available Tables

4 | 11 | -------------------------------------------------------------------------------- /app/views/pages/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @page do |f| %> 2 |
3 | <%= f.label :content, "#{@page.key} Page Content" %> 4 | <%= f.text_area :content, class: "page-content" %> 5 |
6 | <%= f.submit %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/pages/_page.html.erb: -------------------------------------------------------------------------------- 1 | <% markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true, tables: true) %> 2 | 3 |
4 | <%= markdown.render(page.content).html_safe %> 5 |
6 | -------------------------------------------------------------------------------- /app/views/pages/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit Page

2 | 3 |

Use markdown to format the content of the page

4 | 5 | <%= render 'form' %> 6 | -------------------------------------------------------------------------------- /app/views/pages/index.html.erb: -------------------------------------------------------------------------------- 1 |

Select Page to edit

2 | 3 | <% @pages.each do |page| %> 4 |

<%= link_to page.key, edit_page_path(page) %>

5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/shared/_drafts_table.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <% headers.each do |header| %> 5 | 6 | <% end %> 7 | 8 | 9 | 10 | 11 | 12 | <% rows.each do |row| %> 13 | 14 | 20 | 21 | 22 | 23 | 24 | <% columns.each do |column| %> 25 | 26 | <% end %> 27 | 28 | <% end %> 29 | 30 |
<%= header %>
15 | <% if user_signed_in? && current_user.admin? && row.accepted_by_id.nil? && row.denied_by_id.nil? %> 16 | <%= link_to 'Accept', [:accept, row], {class: "button", method: :post} %> 17 | <%= link_to 'Deny', row, {class: "button button-outline", method: :delete} %> 18 | <% end %> 19 | <%= link_to 'Show', row %><%= truncate((row.info[column.to_s] || row.record && row.record.send(column)).to_s) %>
31 | 32 | 39 | -------------------------------------------------------------------------------- /app/views/shared/_show.html.erb: -------------------------------------------------------------------------------- 1 | <% columns.each_with_index do |column, index| %> 2 |

3 | <%= headers[index] %>: 4 | <%= record.public_send(column) %> 5 |

6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/shared/_table.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <% headers.each do |header| %> 6 | 7 | <% end %> 8 | 9 | 10 | 11 | 12 | <% rows.each do |row| %> 13 | 14 | 15 | <% columns.each do |column| %> 16 | 17 | <% end %> 18 | 19 | <% end %> 20 | 21 |
<%= header %>
<%= link_to 'Show', row %> / <%= link_to 'Update', [:edit, row] %><%= truncate(row.public_send(column).to_s) %>
22 | 23 | 31 | -------------------------------------------------------------------------------- /app/views/shelters/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: shelter, local: true) do |f| %> 2 | <% if shelter.errors.any? %> 3 |
4 |

<%= pluralize(shelter.errors.count, "error") %> prohibited this shelter from being saved:

5 | 6 | 11 |
12 | <% end %> 13 | 14 | 15 |
16 | 17 |
18 |
19 | 20 | <%= f.label :google_place, 'Where is it?' %> 21 | <%= f.text_field :google_place, :value => f.object.address, :class => 'place-autocomplete', :data => { 22 | :name => '#geocode-name', 23 | :address => '#geocode-address', 24 | :phone => '#geocode-phone', 25 | :lat => '#geocode-lat', 26 | :lng => '#geocode-lng', 27 | :placeid => '#geocode-place-id', 28 | :city => '#geocode-city', 29 | :county => '#geocode-county', 30 | :state => '#geocode-state', 31 | :mapselector => '#map' 32 | }, :required => true, :autofocus => true %> 33 | 34 | <%= f.label :shelter %> 35 | <%= f.text_field :shelter, :required => true, :id => 'geocode-name' %> 36 | <%= f.label :phone %> 37 | <%= f.text_field :phone, :required => true, :id => 'geocode-phone' %> 38 |
39 |
40 | 41 |
42 | 43 | <% if shelter.latitude && shelter.longitude %> 44 | 54 | <% end %> 55 |
56 |
57 | <%= f.label :source %> 58 | <%= f.text_field :source %> 59 | 60 | <%= f.label :pets %> 61 | <%= f.text_area :pets %> 62 | 63 | <%= f.label :supply_needs %> 64 | <%= f.text_area :supply_needs %> 65 | 66 | <%= f.label :volunteer_needs %> 67 | <%= f.text_area :volunteer_needs %> 68 | 69 | <%= f.label :notes %> 70 | <%= f.text_area :notes %> 71 | 72 | <%= f.label :distribution_center %> 73 | <%= f.text_field :distribution_center %> 74 | 75 | <%= f.label :food_pantry %> 76 | <%= f.text_field :food_pantry %> 77 | 78 | <%= f.hidden_field :address, :id => 'geocode-address' %> 79 | <%= f.hidden_field :city, :id => 'geocode-city' %> 80 | <%= f.hidden_field :county, :id => 'geocode-county' %> 81 | <%= f.hidden_field :state, :id => 'geocode-state' %> 82 | <%= f.hidden_field :latitude, :id => 'geocode-lat' %> 83 | <%= f.hidden_field :longitude, :id => 'geocode-lng' %> 84 | 85 | <% if user_signed_in? && current_user.admin? %> 86 | <%= f.label :private_notes %> 87 | <%= f.text_area :private_notes %> 88 | <% end %> 89 | 90 | <%= f.hidden_field :google_place_id, :id => 'geocode-place-id' %> 91 | <%= f.button :submit %> 92 |
93 | <% end %> 94 | -------------------------------------------------------------------------------- /app/views/shelters/drafts.html.erb: -------------------------------------------------------------------------------- 1 |

Shelters Update Queue

2 | 3 | <%= render "shared/drafts_table", rows: @drafts, columns: @columns, headers: @headers %> 4 | -------------------------------------------------------------------------------- /app/views/shelters/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= @shelter.shelter %>

2 | 3 | <%= render 'form', shelter: @shelter %> 4 | -------------------------------------------------------------------------------- /app/views/shelters/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render @page %> 2 |

Shelters

3 | 4 | 27 | 28 | <%= link_to 'Add New Shelter', new_shelter_path, class: "button button-outline" %> 29 | 30 | <%= render "shared/table", rows: @shelters, headers: @headers, columns: @columns %> 31 | -------------------------------------------------------------------------------- /app/views/shelters/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Shelter

2 | 3 | <%= render 'form', shelter: @shelter %> 4 | -------------------------------------------------------------------------------- /app/views/shelters/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @shelter.shelter %>

2 | 3 | 4 | <%= link_to 'Update', [:edit, @shelter], class: "button button-clear" %> | 5 | <%= link_to 'Back', shelters_path, class: "button button-clear" %> 6 | <% if user_signed_in? && current_user.admin? %> | 7 | <%= link_to 'Archive', archive_shelter_path(@shelter), 8 | method: :post, 9 | data: { confirm: 'Are you sure?' }, 10 | class: "button button-clear" %> 11 | <% end %> 12 | 13 |
14 |
15 | <%= render "shared/show", record: @shelter, headers: @headers, columns: @columns %> 16 |
17 |
18 |
19 | 29 |
30 |
31 | -------------------------------------------------------------------------------- /app/views/splash/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render @page %> 2 | -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |

Users

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <% @users.each do |user| %> 14 | 15 | 16 | 17 | 18 | 19 | <% end %> 20 | 21 |
EmailAdmin?
<%= user.email %><%= user.admin? ? "Yes" : "No" %><%= link_to "View", user %>
22 | 23 | 30 | -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @user.email %>

2 |

Admin: <%= @user.admin? ? "Yes" : "No" %>

3 | 4 | <% if @user.admin? %> 5 | <%= link_to "Revoke Admin", user_path(id: @user.id, admin: 0), class: "button", method: :put %> 6 | <% else %> 7 | <%= link_to "Make Admin", user_path(id: @user.id, admin: 1), class: "button", method: :put %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/users/update.html.erb: -------------------------------------------------------------------------------- 1 |

Users#update

2 |

Find me in app/views/users/update.html.erb

3 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module HarveyApi 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | config.middleware.insert_before 0, Rack::Cors do 19 | allow do 20 | origins '*' 21 | resource '*', :headers => :any, :methods => [:get, :post, :options] 22 | end 23 | end 24 | 25 | config.active_job.queue_adapter = :sucker_punch 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: redis 3 | url: redis://127.0.0.1:6379/1 4 | channel_prefix: harvey-api_development 5 | 6 | test: 7 | adapter: redis 8 | url: redis://127.0.0.1:6379/2 9 | channel_prefix: harvey-api_test 10 | 11 | production: 12 | adapter: redis 13 | url: <%= ENV["REDIS_URL"] %> 14 | channel_prefix: harvey-api_production 15 | -------------------------------------------------------------------------------- /config/database.yml.travis: -------------------------------------------------------------------------------- 1 | test: 2 | adapter: postgresql 3 | database: travis_ci_test 4 | username: postgres 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | end 55 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/geocoder.rb: -------------------------------------------------------------------------------- 1 | Geocoder.configure( 2 | # Geocoding options 3 | # timeout: 3, # geocoding service timeout (secs) 4 | lookup: :google, # name of geocoding service (symbol) 5 | # ip_lookup: :freegeoip, # name of IP address geocoding service (symbol) 6 | # language: :en, # ISO-639 language code 7 | use_https: true, # use HTTPS for lookup requests? (if supported) 8 | # http_proxy: nil, # HTTP proxy server (user:pass@host:port) 9 | # https_proxy: nil, # HTTPS proxy server (user:pass@host:port) 10 | api_key: ENV.fetch('GOOGLE_GEOCODER_API_KEY', ""), # API key for geocoding service 11 | # cache: nil, # cache object (must respond to #[], #[]=, and #del) 12 | # cache_prefix: 'geocoder:', # prefix (string) to use for all cache keys 13 | 14 | # Exceptions that should not be rescued by default 15 | # (if you want to implement custom error handling); 16 | # supports SocketError and Timeout::Error 17 | # always_raise: [], 18 | 19 | # Calculation options 20 | # units: :mi, # :km for kilometers or :mi for miles 21 | # distances: :linear # :spherical or :linear 22 | ) 23 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/traffic_cop.rb: -------------------------------------------------------------------------------- 1 | require "connection_pool" 2 | 3 | # The amazon product advertising API rate limits us pretty hard 4 | # So we need the connection_pool and redis to count our req/s 5 | # And the trafic control will re-schedule jobs 6 | ActiveJob::TrafficControl.client = ConnectionPool.new(size: 5, timeout: 5) { Redis.new } 7 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | "yes": 'Yes' 4 | "no": 'No' 5 | required: 6 | text: 'required' 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: "Please review the problems below:" 13 | # Examples 14 | # labels: 15 | # defaults: 16 | # password: 'Password' 17 | # user: 18 | # new: 19 | # email: 'E-mail to sign in.' 20 | # edit: 21 | # email: 'E-mail.' 22 | # hints: 23 | # defaults: 24 | # username: 'User name to sign in.' 25 | # password: 'No special characters, please.' 26 | # include_blanks: 27 | # defaults: 28 | # age: 'Rather not say' 29 | # prompts: 30 | # defaults: 31 | # age: 'Select your age' 32 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | devise_for :users 4 | 5 | resources :users, only: [:index, :show, :update] 6 | resources :charitable_organizations do 7 | get :drafts, on: :collection 8 | post :archive, on: :member 9 | end 10 | resources :shelters do 11 | get :drafts, on: :collection 12 | post :archive, on: :member 13 | end 14 | resources :needs do 15 | get :drafts, on: :collection 16 | post :archive, on: :member 17 | end 18 | resources :drafts, only: [:show, :destroy] do 19 | post :accept, on: :member 20 | end 21 | resources :amazon_products, except: [:new, :create, :destroy] 22 | namespace :connect do 23 | resources :flagged_markers, only: [:index, :show, :update, :destroy] 24 | end 25 | 26 | root to: "splash#index" 27 | 28 | namespace :api, defaults: { format: :json } do 29 | namespace :v1 do 30 | 31 | get "/needs" => 'needs#index' 32 | get "/shelters" => 'shelters#index' 33 | get "/products" => 'amazon_products#index' 34 | get "/charitable_organizations" => 'charitable_organizations#index' 35 | 36 | namespace :connect do 37 | resources :markers, only: [:create, :index, :update] do 38 | resource :flag, only: :create 39 | end 40 | resources :categories, only: [:index] 41 | end 42 | end 43 | end 44 | 45 | resources :pages, only: [:index, :edit, :update, :destroy] 46 | 47 | # API JSON v2 48 | namespace :api, defaults: { format: :json } do 49 | namespace :v2 do 50 | get "/routes" => 'locations#routes' 51 | get "/:organization/:legacy_table_name" => 'locations#index', as: "api_locations" 52 | get "/:organization/:legacy_table_name/help" => 'locations#help', as: "api_locations_help" 53 | end 54 | end 55 | # HTML Pages v2 Locations 56 | get ":organization/:legacy_table_name", to: "locations#index", as: "locations" 57 | post ":organization/:legacy_table_name", to: "locations#create" 58 | get ":organization/:legacy_table_name/new", to: "locations#new", as: "new_location" 59 | get ":organization/:legacy_table_name/drafts", to: "locations#drafts", as: "drafts_locations" 60 | get ":organization/:legacy_table_name/:id/edit", to: "locations#edit", as: "edit_location" 61 | post ":organization/:legacy_table_name/:id/archive", to: "locations#archive", as: "archive_location" 62 | get ":organization/:legacy_table_name/:id", to: "locations#show", as: "location" 63 | put ":organization/:legacy_table_name/:id", to: "locations#update" 64 | # HTML Pages v2 Location Drafts 65 | post ":organization/:legacy_table_name/drafts/:id/accept", to: "location_drafts#accept", as: "accept_location_draft" 66 | get ":organization/:legacy_table_name/drafts/:id", to: "location_drafts#show", as: "location_draft" 67 | delete ":organization/:legacy_table_name/drafts/:id", to: "location_drafts#destroy" 68 | # HTML Pages v2 Organizations 69 | get ":organization", to: "organizations#show", as: "organization" 70 | 71 | 72 | end 73 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: d22cd95910c4425ab8adfd2aa968b845775c6ffff23f9cdb337bc6f210199ca97736ed261d06bf69242f6483a8e6c474ab88f02dabb9865f9911ff215da31695 22 | 23 | test: 24 | secret_key_base: 44caab1bd8c8d8cf74981bfc824b9c0a1786d7bc3a2d6b09f493f69b3fdd0c76b19d1e587545be50f8f4ae3fd66506b8b113a00a98ae7f8e7b00bf4b21690aeb 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20170831193310_create_shelters.rb: -------------------------------------------------------------------------------- 1 | class CreateShelters < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :shelters do |t| 4 | 5 | t.string :county 6 | t.string :shelter 7 | t.string :address 8 | t.string :city 9 | t.string :pets 10 | t.string :phone 11 | t.boolean :accepting 12 | t.string :last_updated 13 | t.string :updated_by 14 | t.string :notes 15 | t.string :volunteer_needs 16 | t.string :longitude 17 | t.string :latitude 18 | t.string :supply_needs 19 | t.string :source 20 | t.string :address_name 21 | 22 | t.timestamps 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /db/migrate/20170831193314_create_needs.rb: -------------------------------------------------------------------------------- 1 | class CreateNeeds < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :needs do |t| 4 | 5 | t.string :updated_by 6 | t.string :timestamp 7 | t.string :location_name 8 | t.string :location_address 9 | t.string :longitude 10 | t.string :latitude 11 | t.string :contact_for_this_location_name 12 | t.string :contact_for_this_location_phone_number 13 | t.boolean :are_volunteers_needed 14 | t.string :tell_us_about_the_volunteer_needs 15 | t.boolean :are_supplies_needed 16 | t.string :tell_us_about_the_supply_needs 17 | t.string :anything_else_you_would_like_to_tell_us 18 | t.string :source 19 | t.timestamps 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20170831220720_change_lat_long_to_floats.rb: -------------------------------------------------------------------------------- 1 | class ChangeLatLongToFloats < ActiveRecord::Migration[5.1] 2 | def change 3 | execute("DELETE FROM shelters") 4 | execute("DELETE FROM needs") 5 | 6 | execute("ALTER TABLE shelters ALTER COLUMN latitude TYPE float USING (latitude::float)") 7 | execute("ALTER TABLE shelters ALTER COLUMN longitude TYPE float USING (longitude::float)") 8 | execute("ALTER TABLE needs ALTER COLUMN latitude TYPE float USING (latitude::float)") 9 | execute("ALTER TABLE needs ALTER COLUMN longitude TYPE float USING (longitude::float)") 10 | 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20170901185804_add_update_draft_table.rb: -------------------------------------------------------------------------------- 1 | class AddUpdateDraftTable < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :drafts do |t| 4 | t.jsonb :info 5 | t.timestamps 6 | end 7 | 8 | add_reference(:drafts, :record, polymorphic: true) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20170901194923_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :users do |t| 4 | # Admin 5 | t.boolean :admin 6 | 7 | ## Database authenticatable 8 | t.string :email, null: false, default: "" 9 | t.string :encrypted_password, null: false, default: "" 10 | 11 | ## Recoverable 12 | t.string :reset_password_token 13 | t.datetime :reset_password_sent_at 14 | 15 | ## Rememberable 16 | t.datetime :remember_created_at 17 | 18 | ## Trackable 19 | t.integer :sign_in_count, default: 0, null: false 20 | t.datetime :current_sign_in_at 21 | t.datetime :last_sign_in_at 22 | t.inet :current_sign_in_ip 23 | t.inet :last_sign_in_ip 24 | 25 | ## Confirmable 26 | t.string :confirmation_token 27 | t.datetime :confirmed_at 28 | t.datetime :confirmation_sent_at 29 | t.string :unconfirmed_email # Only if using reconfirmable 30 | 31 | ## Lockable 32 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 33 | # t.string :unlock_token # Only if unlock strategy is :email or :both 34 | # t.datetime :locked_at 35 | 36 | 37 | t.timestamps null: false 38 | end 39 | 40 | add_index :users, :email, unique: true 41 | add_index :users, :reset_password_token, unique: true 42 | add_index :users, :confirmation_token, unique: true 43 | # add_index :users, :unlock_token, unique: true 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /db/migrate/20170901201514_create_amazon_products.rb: -------------------------------------------------------------------------------- 1 | class CreateAmazonProducts < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :amazon_products do |t| 4 | t.string :need, unique: true, allow_null: false 5 | t.string :amazon_title, allow_null: false 6 | t.string :asin, allow_null: false 7 | t.string :detail_url, allow_null: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20170901224459_add_accept_deny_draft.rb: -------------------------------------------------------------------------------- 1 | class AddAcceptDenyDraft < ActiveRecord::Migration[5.1] 2 | def change 3 | add_reference :drafts, :accepted_by, {foreign_key: {to_table: :users}} 4 | add_reference :drafts, :denied_by, {foreign_key: {to_table: :users}} 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170902123713_add_disabled_to_amazon_products.rb: -------------------------------------------------------------------------------- 1 | class AddDisabledToAmazonProducts < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :amazon_products, :priority, :boolean, default: false 4 | add_column :amazon_products, :disabled, :boolean, default: false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170902140452_create_ignored_amazon_product_needs.rb: -------------------------------------------------------------------------------- 1 | class CreateIgnoredAmazonProductNeeds < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :ignored_amazon_product_needs do |t| 4 | t.string :need 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20170902203116_add_archive_to_need.rb: -------------------------------------------------------------------------------- 1 | class AddArchiveToNeed < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :needs, :active, :boolean, default: true 4 | add_column :shelters, :active, :boolean, default: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170902213139_create_mucked_homes.rb: -------------------------------------------------------------------------------- 1 | class CreateMuckedHomes < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :mucked_homes do |t| 4 | t.string :name 5 | t.text :description 6 | t.string :phone 7 | t.string :pin 8 | t.float :latitude 9 | t.float :longitude 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20170902222739_create_volunteers.rb: -------------------------------------------------------------------------------- 1 | class CreateVolunteers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :volunteers do |t| 4 | t.string :name 5 | t.text :description 6 | t.string :phone 7 | t.string :pin 8 | t.float :latitude 9 | t.float :longitude 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20170903113333_create_connect_markers.rb: -------------------------------------------------------------------------------- 1 | class CreateConnectMarkers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :connect_markers do |t| 4 | t.string :marker_type, null: false 5 | t.string :name, null: false, default: "" 6 | t.text :description, null: false, default: "" 7 | t.string :phone, null: false, default: "" 8 | t.string :category, null: false, default: "" 9 | t.boolean :resolved, null: false, default: false 10 | t.float :latitude, null: false, default: 0.0 11 | t.float :longitude, null: false, default: 0.0 12 | t.string :address, null: false, default: "" 13 | 14 | t.timestamps 15 | end 16 | add_index :connect_markers, [:latitude, :longitude] 17 | add_index :connect_markers, :category 18 | add_index :connect_markers, :resolved, name: "index_connect_markers_on_unresolved", where: "(resolved IS FALSE)" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20170903230413_create_charitable_organizations.rb: -------------------------------------------------------------------------------- 1 | class CreateCharitableOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :charitable_organizations do |t| 4 | t.string :name 5 | t.string :services 6 | t.boolean :food_bank 7 | t.string :donation_website 8 | t.string :phone_number 9 | t.string :email 10 | t.string :physical_address 11 | t.string :city 12 | t.string :state 13 | t.string :zip 14 | 15 | t.timestamps 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20170903231428_add_archive_to_charitable_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddArchiveToCharitableOrganizations < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :charitable_organizations, :active, :boolean, default: true, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170904025322_add_email_to_connect_markers.rb: -------------------------------------------------------------------------------- 1 | class AddEmailToConnectMarkers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :connect_markers, :email, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170904165336_add_product_categories_to_amazon_products.rb: -------------------------------------------------------------------------------- 1 | class AddProductCategoriesToAmazonProducts < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :amazon_products, :category_specific, :string, default: "" 4 | add_column :amazon_products, :category_general, :string, default: "" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170905114546_add_data_to_markers.rb: -------------------------------------------------------------------------------- 1 | class AddDataToMarkers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :connect_markers, :data, :jsonb, null: false, default: {} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170905201043_create_pages.rb: -------------------------------------------------------------------------------- 1 | class CreatePages < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :pages do |t| 4 | t.string :key, default: "", unique: true, null: false 5 | t.text :content, default: "", null: false 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20170906160409_add_amazon_price_to_products.rb: -------------------------------------------------------------------------------- 1 | class AddAmazonPriceToProducts < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :amazon_products, :price_in_cents, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170907032253_add_fields_to_shelter.rb: -------------------------------------------------------------------------------- 1 | class AddFieldsToShelter < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :shelters, :private_notes, :text 4 | add_column :shelters, :distribution_center, :text 5 | add_column :shelters, :food_pantry, :text 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20170907070934_add_location_table.rb: -------------------------------------------------------------------------------- 1 | class AddLocationTable < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :locations do |t| 4 | # Core location information 5 | t.string :name 6 | t.string :address 7 | t.string :city 8 | t.string :state 9 | t.string :zip 10 | t.string :phone 11 | t.boolean :active, default: true, null: false 12 | # Storage for legacy information 13 | t.string :organization 14 | t.string :legacy_table_name 15 | t.jsonb :legacy_data, default: {}, null: false 16 | t.float :latitude 17 | t.float :longitude 18 | 19 | t.timestamps 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20170907152426_add_device_uuid_connect_markers.rb: -------------------------------------------------------------------------------- 1 | class AddDeviceUuidConnectMarkers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :connect_markers, :device_uuid, :string, null: false, default: '' 4 | add_index :connect_markers, :device_uuid 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170908152859_update_category_on_connect_markers.rb: -------------------------------------------------------------------------------- 1 | class UpdateCategoryOnConnectMarkers < ActiveRecord::Migration[5.1] 2 | def change 3 | # This will lose data, but we don't need/want it anyways 4 | remove_column :connect_markers, :category, :string, null: false, default: "" 5 | add_column :connect_markers, :categories, :jsonb, null: false, default: {} 6 | add_index :connect_markers, :categories, using: :gin 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20170911052811_add_state_to_shelter.rb: -------------------------------------------------------------------------------- 1 | class AddStateToShelter < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :shelters, :state, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170911205637_add_indexes.rb: -------------------------------------------------------------------------------- 1 | class AddIndexes < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :shelters, :active 4 | add_index :shelters, :accepting 5 | add_index :shelters, [:latitude, :longitude] 6 | add_index :needs, :active 7 | add_index :needs, [:latitude, :longitude] 8 | add_index :locations, :active 9 | add_index :locations, [:latitude, :longitude] 10 | add_index :charitable_organizations, :active 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20170911210028_add_calculated_values_for_shelters.rb: -------------------------------------------------------------------------------- 1 | class AddCalculatedValuesForShelters < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :shelters, :calculated_needs, :string, array: true 4 | add_column :shelters, :calculated_updated_at_rfc3339, :string 5 | add_column :shelters, :calculated_phone, :string 6 | 7 | add_column :needs, :calculated_needs, :string, array: true 8 | add_column :needs, :calculated_updated_at_rfc3339, :string 9 | add_column :needs, :calculated_phone, :string 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20170920210424_add_index_to_data_on_connect_markers.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToDataOnConnectMarkers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :connect_markers, :data, using: :gin 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | amazon_json = JSON.parse File.read("#{Rails.root}/db/seeds/amazon_products.json") 2 | amazon_json.each do |json| 3 | ap = AmazonProduct.where(need: json["need"]).first_or_initialize 4 | ap.attributes = json 5 | ap.save 6 | end 7 | 8 | ignored_json = JSON.parse File.read("#{Rails.root}/db/seeds/ignored_amazon_product_needs.json") 9 | ignored_json.each do |json| 10 | iapn= IgnoredAmazonProductNeed.where(need: json["need"]).first_or_initialize 11 | iapn.attributes = json 12 | iapn.save 13 | end 14 | 15 | 16 | Page.home.first_or_create content: "#Welcome" 17 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/data.rake: -------------------------------------------------------------------------------- 1 | namespace :data do 2 | desc "Calculate Shelter and Needs values" 3 | task :calculate_values => :environment do 4 | 5 | Shelter.find_each do |shelter| 6 | shelter.calculate_values 7 | shelter.save 8 | end 9 | 10 | Need.find_each do |need| 11 | need.calculate_values 12 | need.save 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tasks/import.rake: -------------------------------------------------------------------------------- 1 | namespace :api do 2 | desc "Schedule import of records for needs and shelters" 3 | task :import => :environment do 4 | if Rails.env.development? 5 | ImportSheltersJob.perform_now 6 | ImportNeedsJob.perform_now 7 | else 8 | puts "Not running api:import because it's not development mode. Mode: #{Rails.env}" 9 | end 10 | end 11 | end 12 | 13 | namespace :amazon do 14 | desc "Schedule import of Amazon Products" 15 | task :import => :environment do 16 | ScheduleAmazonFetchJob.perform_now 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/templates/erb/scaffold/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %> 2 | <%%= f.error_notification %> 3 | 4 |
5 | <%- attributes.each do |attribute| -%> 6 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %> 7 | <%- end -%> 8 |
9 | 10 |
11 | <%%= f.button :submit %> 12 |
13 | <%% end %> 14 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "harvey-api", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/api-docs/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/api-docs/favicon-16x16.png -------------------------------------------------------------------------------- /public/api-docs/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/api-docs/favicon-32x32.png -------------------------------------------------------------------------------- /public/api-docs/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 58 | -------------------------------------------------------------------------------- /public/api-docs/swagger-ui-bundle.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAu7LA;;;;;;AA65DA;;;;;;;;;;;;;;;;;;;;;;;;;;AA68TA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AA69pBA;;;;;AA81QA;AAm4DA;;;;;;AAo4YA;;;;;;AA0iaA;AA4lvBA","sourceRoot":""} -------------------------------------------------------------------------------- /public/api-docs/swagger-ui-standalone-preset.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA80CA;;;;;;AAqpFA","sourceRoot":""} -------------------------------------------------------------------------------- /public/api-docs/swagger-ui.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"swagger-ui.css","sources":[],"mappings":"","sourceRoot":""} -------------------------------------------------------------------------------- /public/api-docs/swagger-ui.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAokeA","sourceRoot":""} -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/contributors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Harvey API Contributors 5 | 6 | 9 | 10 | 11 |
12 |

Harvey API Contributors

13 |

Below are a list of our contributors

14 |
15 | 16 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/favicon.ico -------------------------------------------------------------------------------- /public/images/readme/screenshot_create-service-account-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/images/readme/screenshot_create-service-account-key.png -------------------------------------------------------------------------------- /public/images/readme/screenshot_enable_google_sheets_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/images/readme/screenshot_enable_google_sheets_api.png -------------------------------------------------------------------------------- /public/images/readme/screenshot_rails_server_run_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/public/images/readme/screenshot_rails_server_run_test.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/amazon_products_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AmazonProductsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | fixtures :all 7 | 8 | test "products required admin" do 9 | get amazon_products_path 10 | assert_response :redirect 11 | sign_in users(:admin) 12 | get amazon_products_path 13 | assert_response :success 14 | end 15 | 16 | test "load show" do 17 | get amazon_product_path(amazon_products(:baby_formula)) 18 | assert_response :redirect 19 | sign_in users(:admin) 20 | get amazon_product_path(amazon_products(:baby_formula)) 21 | assert_response :success 22 | end 23 | 24 | test "Can update" do 25 | sign_in users(:admin) 26 | patch amazon_product_path(amazon_products(:baby_formula)), 27 | params: { amazon_product: { asin: "NEWASIN", priority: false, disabled: true }} 28 | 29 | product = amazon_products(:baby_formula) 30 | product.reload 31 | assert_equal "NEWASIN", product.asin 32 | assert_equal false, product.priority 33 | assert_equal true, product.disabled 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /test/controllers/api/v1/charitable_organizations_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::CharitableOrganizationsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | fixtures :all 6 | 7 | test "Using If-Modified-Since will 304" do 8 | max = CharitableOrganization.maximum("updated_at") 9 | get "/api/v1/charitable_organizations", headers: { 10 | "If-Modified-Since" => max.rfc2822 11 | } 12 | assert_equal 304, response.status 13 | end 14 | 15 | test "returns all charitable_organizations" do 16 | count = CharitableOrganization.count 17 | get "/api/v1/charitable_organizations" 18 | json = JSON.parse(response.body) 19 | assert_equal count, json["charitable_organizations"].length 20 | assert_equal count, json["meta"]["result_count"] 21 | end 22 | 23 | test "filters are returned" do 24 | count = CharitableOrganization.where(food_bank: true).count 25 | get "/api/v1/charitable_organizations?food_bank=true" 26 | json = JSON.parse(response.body) 27 | assert_equal count, json["charitable_organizations"].length 28 | assert_equal count, json["meta"]["result_count"] 29 | assert_equal "true", json["meta"]["filters"]["food_bank"] 30 | end 31 | 32 | test "charitable_organizations are not returned after they are archived" do 33 | archived = CharitableOrganization.where(active: false).count 34 | active = CharitableOrganization.where(active: true).count 35 | count = active - archived 36 | get "/api/v1/charitable_organizations" 37 | json = JSON.parse(response.body) 38 | assert_equal count, json["charitable_organizations"].length 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/controllers/api/v1/connect/categories_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::V1::Connect::CategoriesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "returns all categories" do 6 | get api_v1_connect_categories_path 7 | json = JSON.parse(response.body) 8 | categories = Rails.application.config_for(:connect_categories) 9 | assert_equal json, categories 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/controllers/api/v1/connect/flags_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class Api::V1::Connect::FlagsControllerTest < ActionDispatch::IntegrationTest 6 | 7 | def default_headers(headers = nil) 8 | headers || { "DisasterConnect-Device-UUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } 9 | end 10 | 11 | test "can flag a marker for inapproiate content" do 12 | marker = connect_markers(:offensive) 13 | assert_difference('Connect::Marker.flagged.count', 1) do 14 | post api_v1_connect_marker_flag_path(marker), 15 | params: { marker_id: marker.id, reason: 'This guy is being a jerk.' }, 16 | headers: default_headers 17 | end 18 | assert_response 204 19 | marker.reload 20 | assert_equal marker.data.dig('inappropriate_flag', 'flagged_for'), 'This guy is being a jerk.' 21 | assert_equal marker.data.dig('inappropriate_flag', 'flagged_by'), "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 22 | end 23 | 24 | test "can not flag a marker without a device uuid" do 25 | marker = connect_markers(:offensive) 26 | post api_v1_connect_marker_flag_path(marker), 27 | params: { marker_id: marker.id, reason: 'This guy is being a jerk.' }, 28 | headers: default_headers({}) 29 | assert_response 403 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/controllers/api/v1/needs_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::NeedsControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "Using If-Modified-Since will 304" do 6 | max = Need.maximum("updated_at") 7 | get "/api/v1/needs", headers: { 8 | "If-Modified-Since" => max.rfc2822 9 | } 10 | assert_equal 304, response.status 11 | end 12 | 13 | test "returns all needs" do 14 | count = Need.count 15 | get "/api/v1/needs" 16 | json = JSON.parse(response.body) 17 | assert_equal count, json["needs"].length 18 | assert_equal count, json["meta"]["result_count"] 19 | end 20 | 21 | test "Geo and limits work" do 22 | get "/api/v1/needs?lat=30.0071377&lon=-95.3797033&limit=1" 23 | json = JSON.parse(response.body) 24 | assert_equal 1, json["needs"].length 25 | assert_equal 1, json["meta"]["result_count"] 26 | end 27 | 28 | test "filters are returned" do 29 | count = Need.where(are_supplies_needed: true).count 30 | get "/api/v1/needs?supplies_needed=true" 31 | json = JSON.parse(response.body) 32 | assert_equal count, json["needs"].length 33 | assert_equal count, json["meta"]["result_count"] 34 | assert_equal "true", json["meta"]["filters"]["supplies_needed"] 35 | end 36 | 37 | test "needs are not returned after they are archived" do 38 | archived = Need.where(active: false).count 39 | active = Need.where(active: !false).count 40 | count = active - archived 41 | get "/api/v1/needs" 42 | json = JSON.parse(response.body) 43 | assert_equal count, json["needs"].length 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/controllers/api/v1/shelters_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::SheltersControllerTest < ActionDispatch::IntegrationTest 4 | 5 | test "Using If-Modified-Since will 304" do 6 | max = Shelter.maximum("updated_at") 7 | get "/api/v1/shelters", headers: { 8 | "If-Modified-Since" => max.rfc2822 9 | } 10 | assert_equal 304, response.status 11 | end 12 | 13 | test "returns all shelters" do 14 | count = Shelter.count 15 | get "/api/v1/shelters" 16 | json = JSON.parse(response.body) 17 | assert_equal count, json["shelters"].length 18 | assert_equal count, json["meta"]["result_count"] 19 | end 20 | 21 | test "Geo and limits work" do 22 | count = Shelter.count 23 | get "/api/v1/shelters?lat=30.0071377&lon=-95.3797033&limit=1" 24 | json = JSON.parse(response.body) 25 | assert_equal shelters(:lonestar).shelter, json["shelters"].first["shelter"] 26 | assert_equal 1, json["shelters"].length 27 | assert_equal 1, json["meta"]["result_count"] 28 | end 29 | 30 | test "filters are returned" do 31 | count = Shelter.where(accepting: true).count 32 | get "/api/v1/shelters?accepting=true" 33 | json = JSON.parse(response.body) 34 | assert_equal count, json["shelters"].length 35 | assert_equal count, json["meta"]["result_count"] 36 | assert_equal "true", json["meta"]["filters"]["accepting"] 37 | end 38 | 39 | test "shelters are not returned after they are archived" do 40 | archived = Shelter.where(active: false).count 41 | active = Shelter.where(active: !false).count 42 | count = active - archived 43 | get "/api/v1/shelters" 44 | json = JSON.parse(response.body) 45 | assert_equal count, json["shelters"].length 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/controllers/connect/flagged_markers_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FlaggedMarkersControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | fixtures :all 7 | 8 | test "only admins can access index" do 9 | get connect_flagged_markers_path 10 | assert_redirected_to root_path 11 | sign_in users(:admin) 12 | get connect_flagged_markers_path 13 | assert_response :success 14 | end 15 | 16 | test "only admins can access show" do 17 | get connect_flagged_marker_path(connect_markers(:flagged)) 18 | assert_redirected_to root_path 19 | sign_in users(:admin) 20 | get connect_flagged_marker_path(connect_markers(:flagged)) 21 | assert_response :success 22 | end 23 | 24 | test "Can clear a marker" do 25 | sign_in users(:admin) 26 | marker = connect_markers(:flagged) 27 | assert(marker.flagged_inappropriate?) 28 | delete connect_flagged_marker_path(marker) 29 | assert_redirected_to connect_flagged_markers_path 30 | marker.reload 31 | refute(marker.flagged_inappropriate?) 32 | end 33 | 34 | test "Can confirm a marker" do 35 | sign_in users(:admin) 36 | marker = connect_markers(:flagged) 37 | assert(marker.flagged_inappropriate?) 38 | refute(marker.resolved?) 39 | patch connect_flagged_marker_path(marker) 40 | assert_redirected_to connect_flagged_markers_path 41 | marker.reload 42 | assert(marker.flagged_inappropriate?) 43 | assert(marker.resolved?) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /test/controllers/drafts_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DraftsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | def drafts 7 | Draft.where("info->'record_type' is not null").all 8 | end 9 | 10 | test "loads show for all types" do 11 | drafts.each do |draft| 12 | get draft_path(draft) 13 | assert_response :success 14 | end 15 | end 16 | 17 | test "accepts drafts for all types" do 18 | drafts.each do |draft| 19 | sign_in(users(:admin)) 20 | post accept_draft_path(draft) 21 | assert_response :redirect 22 | draft = draft.reload 23 | record = draft.record 24 | assert_equal(users(:admin), draft.accepted_by) 25 | # Assert the info on the draft matches the corresponding fields on the record 26 | draft.info.keys.each do |key| 27 | # We use record type to create the record for the draft if it is a new record 28 | # This field does not exist on the record, only in the draft 29 | # You cannot set a polymorphic type without also setting the id (as far as I know) 30 | next if 'record_type' == key 31 | assert_equal(record.send(key), draft.info[key], "Error for #{key} on #{draft}") 32 | end 33 | end 34 | end 35 | 36 | test "denies drafts for all types" do 37 | drafts.each do |draft| 38 | sign_in(users(:admin)) 39 | delete draft_path(draft) 40 | assert_response :redirect 41 | draft = draft.reload 42 | record = draft.record 43 | assert_equal(users(:admin), draft.denied_by) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /test/controllers/location_drafts_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LocationDraftsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | def drafts 7 | Draft.where("info->'organization' is not null").all 8 | end 9 | 10 | test "loads show for all types" do 11 | drafts.each do |draft| 12 | organization = draft.info['organization'] 13 | legacy_table_name = draft.info['legacy_table_name'] 14 | 15 | get location_draft_path(organization: organization, legacy_table_name: legacy_table_name, id: draft.id) 16 | assert_response :success 17 | end 18 | end 19 | 20 | test "accepts drafts for all types" do 21 | drafts.each do |draft| 22 | organization = draft.info['organization'] 23 | legacy_table_name = draft.info['legacy_table_name'] 24 | 25 | sign_in(users(:admin)) 26 | post accept_location_draft_path(organization: organization, legacy_table_name: legacy_table_name, id: draft.id) 27 | assert_response :redirect 28 | draft = draft.reload 29 | record = Location::Whitelist.find(organization, legacy_table_name).find(draft.record_id) 30 | assert_equal(users(:admin), draft.accepted_by) 31 | # Assert the info on the draft matches the corresponding fields on the record 32 | draft.info.keys.each do |key| 33 | # We use record type to create the record for the draft if it is a new record 34 | # This field does not exist on the record, only in the draft 35 | # You cannot set a polymorphic type without also setting the id (as far as I know) 36 | next if 'record_type' == key 37 | if(record.send(key).nil?) 38 | assert_nil(draft.info[key], "Error #{key} not nil for #{draft.info['name']}") 39 | else 40 | assert_equal(record.send(key), draft.info[key], "Error for #{key} on #{draft.info['name']}") 41 | end 42 | end 43 | end 44 | end 45 | 46 | test "denies drafts for all types" do 47 | drafts.each do |draft| 48 | organization = draft.info['organization'] 49 | legacy_table_name = draft.info['legacy_table_name'] 50 | 51 | sign_in(users(:admin)) 52 | delete location_draft_path(organization: organization, legacy_table_name: legacy_table_name, id: draft.id) 53 | assert_response :redirect 54 | draft = draft.reload 55 | record = draft.record 56 | assert_equal(users(:admin), draft.denied_by) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /test/controllers/pages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PagesControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | fixtures :all 7 | 8 | test "should get index if user is admin" do 9 | sign_in users(:admin) 10 | get pages_path 11 | assert_response :success 12 | end 13 | 14 | test "should redirect if user is not admin" do 15 | get pages_path 16 | assert_response :redirect 17 | end 18 | 19 | test "should get edit if user is admin" do 20 | sign_in users(:admin) 21 | get edit_page_path(pages(:home)) 22 | assert_response :success 23 | end 24 | 25 | test "should update if user is admin" do 26 | page = pages(:home) 27 | content = "updated page content" 28 | expected_count = Page.count 29 | sign_in users(:admin) 30 | put page_path(page), params: { page: { content: content }} 31 | page.reload 32 | assert_response :redirect 33 | assert_equal(expected_count, Page.count) 34 | assert_equal(content, page.content) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/amazon_products.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | baby_formula: 4 | need: baby formula 5 | asin: FAKEBABYASIN 6 | amazon_title: Super Great Bebe Formula 7 | detail_url: http://www.example.com/amazon/baby 8 | category_specific: Grocery 9 | category_general: Grocery 10 | priority: true 11 | disabled: false 12 | price_in_cents: 1000 13 | 14 | garbage_bags: 15 | need: garbage bags 16 | asin: FAKEBAGS 17 | amazon_title: BIG BAGS 18 | detail_url: http://www.example.com/amazon/bags 19 | category_specific: Cleanup 20 | category_general: Household 21 | priority: false 22 | disabled: false 23 | price_in_cents: 500 24 | 25 | iphones: 26 | need: iphone 6S 27 | asin: FAKEBAGS 28 | amazon_title: iphones 29 | detail_url: http://www.example.com/amazon/iphones 30 | category_specific: Apple 31 | category_general: Electronics 32 | priority: false 33 | disabled: true 34 | price_in_cents: 100000 35 | -------------------------------------------------------------------------------- /test/fixtures/charitable_organizations.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | name: "Charity One" 5 | services: "Church" 6 | food_bank: false 7 | donation_website: "http://one.com" 8 | phone_number: "123-123-1234" 9 | email: "charity@charity.com" 10 | physical_address: "123 main" 11 | city: "houston" 12 | state: "tx" 13 | zip: "77006" 14 | active: true 15 | 16 | two: 17 | name: "Second Charity" 18 | services: "Something Else" 19 | food_bank: true 20 | donation_website: "https://another.com" 21 | phone_number: "123-123-1235" 22 | email: "321@123.com" 23 | physical_address: "312 Main" 24 | city: "Houston" 25 | state: "tx" 26 | zip: "77007" 27 | active: false 28 | -------------------------------------------------------------------------------- /test/fixtures/connect/markers.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | have: 4 | device_uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 5 | marker_type: have 6 | name: ChaiOne 7 | description: MyText 8 | phone: (888) 316-0357 9 | categories: 10 | labor: 11 | - muck 12 | - rake 13 | housing: 14 | - for 5 people 15 | food: 16 | - pork cracklins 17 | resolved: false 18 | latitude: 29.7488469 19 | longitude: -95.4593152 20 | data: 21 | foo: 22 | - 1 23 | - 2 24 | - 3 25 | bar: baz 26 | 27 | need: 28 | device_uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 29 | marker_type: need 30 | name: Joe Blow 31 | description: Halp 32 | phone: 713-867-5309 33 | categories: 34 | foo: bar 35 | resolved: false 36 | latitude: 29.773700 37 | longitude: -95.358611 38 | 39 | close_need: 40 | device_uuid: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy 41 | marker_type: need 42 | name: Joe Close 43 | description: Halp 44 | phone: 713-867-5309 45 | categories: 46 | foo: bar 47 | resolved: false 48 | latitude: 29.750621 49 | longitude: -95.457776 50 | 51 | resolved: 52 | device_uuid: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy 53 | marker_type: need 54 | name: Resolved 55 | description: Halp 56 | phone: 713-867-5309 57 | categories: 58 | foo: bar 59 | resolved: true 60 | latitude: 29.776 61 | longitude: -95.3587 62 | 63 | flagged: 64 | device_uuid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 65 | marker_type: need 66 | name: Dirty Birdy 67 | description: Some really offensive content 68 | phone: 713-867-5309 69 | categories: 70 | foo: bar 71 | resolved: false 72 | latitude: 29.773700 73 | longitude: -95.358611 74 | data: 75 | inappropriate_flag: 76 | flagged_by: yyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy 77 | flagged_for: Frankly I find the idea of a bug that thinks offensive 78 | flagged_at: <%= Time.zone.now %> 79 | 80 | offensive: 81 | device_uuid: zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz 82 | marker_type: need 83 | name: Dirty Birdy 84 | description: I need to be a jerk 85 | phone: 713-867-5309 86 | categories: 87 | foo: bar 88 | resolved: false 89 | latitude: 29.773700 90 | longitude: -95.358611 -------------------------------------------------------------------------------- /test/fixtures/drafts.yml: -------------------------------------------------------------------------------- 1 | new_need: 2 | info: 3 | record_type: "Need" 4 | location_name: "Grace Goodwill" 5 | location_address: "1301 Fannin St" 6 | are_volunteers_needed: false 7 | tell_us_about_the_volunteer_needs: "" 8 | are_supplies_needed: true 9 | tell_us_about_the_supply_needs: "non-perishable foods cleaning supplies personal care items" 10 | anything_else_you_would_like_to_tell_us: "" 11 | source: "https://example.com" 12 | 13 | update_need: 14 | record: katy (Need) 15 | info: 16 | record_type: "Need" 17 | tell_us_about_the_volunteer_needs: "we need trained medical personel" 18 | 19 | new_shelter: 20 | info: 21 | record_type: "Shelter" 22 | shelter: "Toyota Center " 23 | address: "1001 Fannin St" 24 | city: "Houston" 25 | pets: "Yes Crated only" 26 | phone: "(832) 555-5555" 27 | accepting: false 28 | notes: "none" 29 | volunteer_needs: "Needs volunteers." 30 | supply_needs: "beds, nappies, towels" 31 | source: "https://twitter.com/ToyotaCenter/status/902725937762054145" 32 | address_name: "Toyota Center 1001 Fannin" 33 | 34 | update_shelter: 35 | record: nrg (Shelter) 36 | info: 37 | record_type: "Shelter" 38 | pets: "No" 39 | 40 | new_charitable_organization: 41 | info: 42 | name: "Hope" 43 | services: church 44 | food_bank: false 45 | donation_website: "http://donation.com" 46 | phone_number: "567-234-2342" 47 | email: "hi@hi.co" 48 | physical_address: "123321 main" 49 | city: "humble" 50 | state: "tx" 51 | zip: "12345" 52 | record_type: "CharitableOrganization" 53 | 54 | update_charitable_organization: 55 | record: one (CharitableOrganization) 56 | info: 57 | record_type: "CharitableOrganization" 58 | food_bank: true 59 | 60 | new_rescuee: 61 | info: 62 | name: "Bobby B." 63 | address: "1401 Fannin" 64 | city: "Houston" 65 | state: "TX" 66 | phone: "555-555-5555" 67 | organization: "hurricane-harvey-rescue-dispatchers" 68 | legacy_table_name: "rescuees" 69 | apartment_number: 70 | status: "Boat in Route" 71 | tier: "None" 72 | high_water_vehicle_accessible: false 73 | number_of_adults: "2" 74 | number_of_elderly: "0" 75 | number_of_children: "2" 76 | number_of_pets: "1" 77 | 78 | update_rescuee: 79 | record: family_rescuees (Location) 80 | info: 81 | status: "Rescued" 82 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/fixtures/locations.yml: -------------------------------------------------------------------------------- 1 | rescue_boat: 2 | name: "Jonny 5" 3 | address: "1001 Fannin" 4 | city: "Houston" 5 | state: "TX" 6 | phone: "555-555-5555" 7 | organization: "hurricane-harvey-rescue-dispatchers" 8 | legacy_table_name: "rescuers" 9 | legacy_data: 10 | status: "in route" 11 | vehicle_type: "boat" 12 | capacity: "10" 13 | background_check: true 14 | medic: false 15 | mechanic: false 16 | has_parts: false 17 | 18 | rescue_truck: 19 | name: "Trucky 5" 20 | address: "1101 Fannin" 21 | city: "Houston" 22 | state: "TX" 23 | phone: "555-555-5555" 24 | organization: "hurricane-harvey-rescue-dispatchers" 25 | legacy_table_name: "rescuers" 26 | legacy_data: 27 | status: "returning" 28 | vehicle_type: "high water vehicle" 29 | capacity: "10" 30 | background_check: true 31 | medic: true 32 | mechanic: false 33 | has_parts: false 34 | 35 | elderly_rescuees: 36 | name: "Sam A." 37 | address: "1201 Fannin" 38 | city: "Houston" 39 | state: "TX" 40 | phone: "555-555-5555" 41 | organization: "hurricane-harvey-rescue-dispatchers" 42 | legacy_table_name: "rescuees" 43 | legacy_data: 44 | apartment_number: "A" 45 | status: "Awaiting Rescue" 46 | tier: "Tier 1" 47 | high_water_vehicle_accessable: true 48 | number_of_adults: "0" 49 | number_of_elderly: "2" 50 | number_of_children: "0" 51 | number_of_pets: "0" 52 | notes: "Upstairs" 53 | 54 | family_rescuees: 55 | name: "Barbara A." 56 | address: "1301 Fannin" 57 | city: "Houston" 58 | state: "TX" 59 | phone: "555-555-5555" 60 | organization: "hurricane-harvey-rescue-dispatchers" 61 | legacy_table_name: "rescuees" 62 | legacy_data: 63 | apartment_number: 64 | status: "Boat in Route" 65 | tier: "Tier 2" 66 | high_water_vehicle_accessable: false 67 | number_of_adults: "2" 68 | number_of_elderly: "0" 69 | number_of_children: "2" 70 | number_of_pets: "1" 71 | notes: "on roof" 72 | -------------------------------------------------------------------------------- /test/fixtures/mucked_homes.yml: -------------------------------------------------------------------------------- 1 | alan: 2 | name: "Alan Hong" 3 | description: "NRG Pkwy Houston TX 77054" 4 | longitude: -95.41109 5 | latitude: 29.685072 6 | phone: "(555) 555-5555" 7 | pin: "1234" 8 | 9 | joe: 10 | name: "Joe Shmoe" 11 | description: "123 Main St., Houston TX 77057" 12 | longitude: -95.40009 13 | latitude: 29.658072 14 | phone: "" 15 | pin: "0000" 16 | -------------------------------------------------------------------------------- /test/fixtures/needs.yml: -------------------------------------------------------------------------------- 1 | katy: 2 | updated_by: "Stephanie" 3 | timestamp: "2017-08-30 11:33 AM" 4 | location_name: "Katy Ministries" 5 | location_address: "5601 E 5th Street Katy 77493" 6 | longitude: -95.819327 7 | latitude: 29.789867 8 | contact_for_this_location_name: "" 9 | contact_for_this_location_phone_number: "" 10 | are_volunteers_needed: false 11 | tell_us_about_the_volunteer_needs: "" 12 | are_supplies_needed: true 13 | tell_us_about_the_supply_needs: "baby formula" 14 | anything_else_you_would_like_to_tell_us: "" 15 | source: "https://www.facebook.com/KatyChristianMinistries/posts/1825892877424111" 16 | 17 | victory: 18 | updated_by: "Brisa" 19 | timestamp: "8/31/2017 12:30:35" 20 | location_name: "Victory Christian Center" 21 | location_address: "11420 Cutten Rd Houston Texas TX 77066" 22 | longitude: -95.51761 23 | latitude: 29.947117 24 | contact_for_this_location_name: "" 25 | contact_for_this_location_phone_number: "(281) 587-1110" 26 | are_volunteers_needed: false 27 | tell_us_about_the_volunteer_needs: "" 28 | are_supplies_needed: true 29 | tell_us_about_the_supply_needs: "open until 6pm:" 30 | anything_else_you_would_like_to_tell_us: "" 31 | source: "https://www.facebook.com/VCCHouston/" 32 | -------------------------------------------------------------------------------- /test/fixtures/pages.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | home: 4 | key: home 5 | content: This should show on the homepage! 6 | -------------------------------------------------------------------------------- /test/fixtures/shelters.yml: -------------------------------------------------------------------------------- 1 | nrg: 2 | shelter: "NRG Center " 3 | address: "NRG Pkwy Houston TX 77054" 4 | city: "Houston" 5 | pets: "Yes Dogs and Cats only" 6 | phone: "(832) 667-1400" 7 | accepting: true 8 | last_updated: "2017-08-30 10:25 PM" 9 | updated_by: "Lynn" 10 | notes: "Will be taking 10000. Working with Baker-Ripley (NOT the Red Cross). See Houston Chronicle article link in the comment." 11 | volunteer_needs: "Needs volunteers." 12 | longitude: -95.41109 13 | latitude: 29.685072 14 | supply_needs: "blankets clothing sanitary etc" 15 | source: "Https://twitter.com/BakerRipley/status/902725937762054145" 16 | address_name: "NRG Center NRG Pkwy Houston TX 77054 Houston TX" 17 | 18 | lonestar: 19 | county: "Harris" 20 | shelter: "Lone Star College North Harris" 21 | address: "2700 W W Thorne Dr" 22 | city: "Houston" 23 | pets: "" 24 | phone: "(281) 618-5400" 25 | accepting: false 26 | last_updated: "2017-08-29 03:27 PM" 27 | updated_by: "Doostee" 28 | notes: " Shelter at capacity. " 29 | volunteer_needs: "Volunteers needed. Here 8/30 4:30pm." 30 | longitude: -95.3797033 31 | latitude: 30.0071377 32 | supply_needs: " Juice boxes for kids towels travel-sized toiletries – shampoo toothpaste toothbrushes pet kennels leashes packaged snacks and ext. cords/power strips" 33 | source: "https://t.co/REjWR4UR7Q" 34 | address_name: "Lone Star College North Harris 2700 W W Thorne Dr Houston TX" 35 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | admin: 8 | email: admin@example.com 9 | admin: true 10 | 11 | guest: 12 | email: guest@example.com 13 | -------------------------------------------------------------------------------- /test/fixtures/volunteers.yml: -------------------------------------------------------------------------------- 1 | john: 2 | name: "John Doe" 3 | description: "8181 Fannin St., 77054" 4 | phone: "(555)555-5555" 5 | pin: "0000" 6 | longitude: -95.41109 7 | latitude: 29.685072 8 | 9 | jane: 10 | name: "Jane Doe" 11 | description: "123 Holcombe Blvd., Houston TX 77056" 12 | phone: "" 13 | pin: "1234" 14 | longitude: -95.42309 15 | latitude: 29.674072 16 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/integration/.keep -------------------------------------------------------------------------------- /test/jobs/fetch_amazon_product_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'vcr' 3 | 4 | VCR.configure do |config| 5 | config.cassette_library_dir = "test/vcr_cassettes" 6 | config.hook_into :excon 7 | end 8 | 9 | def sanitize_qs query_string 10 | hash = CGI::parse query_string 11 | hash.delete "Signature" 12 | hash.delete "Timestamp" 13 | hash 14 | end 15 | 16 | class FetchAmazonProductJobTest < ActiveJob::TestCase 17 | 18 | # What are we doing here? The Amazon URLS have a timestamp and signature 19 | # and they change constantly. So let's compare bsed on without those 20 | # sorry for this, but don't want to have it reach out to amazon on every 21 | # test run 22 | amazon_url_matcher = lambda do |request_1, request_2| 23 | url_1 = URI(request_1.uri) 24 | url_2 = URI(request_2.uri) 25 | (url_1.path == url_2.path ) && 26 | (url_1.host == url_2.host ) && 27 | (sanitize_qs(url_1.query) == sanitize_qs(url_2.query)) 28 | end 29 | 30 | 31 | test "Creates correct amazon product" do 32 | 33 | VCR.use_cassette("amazon", :match_requests_on => [:method, amazon_url_matcher]) do 34 | 35 | FetchAmazonProductJob.perform_now "trash bags (kitchen and contractor size)" 36 | assert AmazonProduct.pluck(:need).include? "trash bags (kitchen and contractor size)" 37 | FetchAmazonProductJob.perform_now "baby formula" 38 | assert AmazonProduct.pluck(:need).include? "baby formula" 39 | 40 | end 41 | end 42 | 43 | test "updates if already exists" do 44 | 45 | VCR.use_cassette("amazon", :allow_playback_repeats => true, :match_requests_on => [:method, amazon_url_matcher]) do 46 | AmazonProduct.destroy_all 47 | FetchAmazonProductJob.perform_now "trash bags (kitchen and contractor size)" 48 | FetchAmazonProductJob.perform_now "trash bags (kitchen and contractor size)" 49 | assert_equal 1, AmazonProduct.count 50 | end 51 | end 52 | 53 | test "If no result found, create ignored request" do 54 | 55 | VCR.use_cassette("amazon-missing", :allow_playback_repeats => true, :match_requests_on => [:method, amazon_url_matcher]) do 56 | AmazonProduct.destroy_all 57 | IgnoredAmazonProductNeed.destroy_all 58 | FetchAmazonProductJob.perform_now "987345jh4nksad.hy98asd67fsjsd" 59 | assert_equal 0, AmazonProduct.count 60 | assert_equal 1, IgnoredAmazonProductNeed.count 61 | end 62 | end 63 | 64 | end 65 | -------------------------------------------------------------------------------- /test/jobs/import_needs_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'vcr' 3 | 4 | VCR.configure do |config| 5 | config.cassette_library_dir = "test/vcr_cassettes" 6 | config.hook_into :webmock 7 | end 8 | 9 | class ImportNeedsJobTest < ActiveJob::TestCase 10 | 11 | setup do 12 | Need.destroy_all 13 | end 14 | 15 | test "imports the right amount of data" do 16 | VCR.use_cassette("needs") do 17 | needs = APIImporter.needs 18 | ImportNeedsJob.perform_now 19 | needsCount = needs.count 20 | recordsCount = Need.count 21 | assert_equal needsCount, recordsCount 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/jobs/import_sheet_data_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ImportSheetDataJobTest < ActiveJob::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/jobs/import_shelters_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'vcr' 3 | 4 | VCR.configure do |config| 5 | config.cassette_library_dir = "test/vcr_cassettes" 6 | config.hook_into :webmock 7 | end 8 | 9 | class ImportSheltersJobTest < ActiveJob::TestCase 10 | 11 | setup do 12 | Shelter.destroy_all 13 | end 14 | 15 | test "imports the right amount of data" do 16 | VCR.use_cassette("shelters") do 17 | shelters = APIImporter.shelters 18 | ImportSheltersJob.perform_now 19 | sheltersCount = shelters.count 20 | recordsCount = Shelter.count 21 | assert_equal sheltersCount, recordsCount 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/jobs/schedule_amazon_fetch_job_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ScheduleAmazonFetchJobTest < ActiveJob::TestCase 4 | 5 | setup do 6 | Need.destroy_all 7 | AmazonProduct.destroy_all 8 | end 9 | 10 | test "schedules for the correct need" do 11 | Need.create! tell_us_about_the_supply_needs: "trash bags (kitchen and contractor size)" 12 | 13 | assert_enqueued_with(job: FetchAmazonProductJob, args: ["trash bags (kitchen and contractor size)"]) do 14 | ScheduleAmazonFetchJob.perform_now 15 | end 16 | end 17 | 18 | test "schedules for each need" do 19 | Need.create! tell_us_about_the_supply_needs: "opens at 6pm\n*trash bags (kitchen and contractor size), baby formula" 20 | 21 | assert_enqueued_jobs 2 do 22 | ScheduleAmazonFetchJob.perform_now 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/models/.keep -------------------------------------------------------------------------------- /test/models/amazon_product_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AmazonProductTest < ActiveSupport::TestCase 4 | 5 | test ":active does not include disabled" do 6 | AmazonProduct.destroy_all 7 | formula = AmazonProduct.create! price_in_cents: 2000, need: "baby formula", asin: "XXXXXX", detail_url: "http", category_general: "baby", category_specific: "baby food" 8 | AmazonProduct.create! price_in_cents: 100, disabled: true, need: "opens at 6", asin: "XXXXXX", detail_url: "http" 9 | assert_equal [formula], AmazonProduct.active 10 | end 11 | 12 | test ":active does not include missing categories" do 13 | AmazonProduct.destroy_all 14 | formula = AmazonProduct.create! need: "baby formula", asin: "XXXXXX", detail_url: "http" 15 | assert_equal [], AmazonProduct.active 16 | end 17 | 18 | test ":active does not include missing pricing" do 19 | AmazonProduct.destroy_all 20 | formula = AmazonProduct.create! price_in_cents: 2000, need: "baby formula", asin: "XXXXXX", detail_url: "http", category_general: "baby", category_specific: "baby food" 21 | AmazonProduct.create! price_in_cents: 0, disabled: true, need: "opens at 6", asin: "XXXXXX", detail_url: "http" 22 | assert_equal [formula], AmazonProduct.active 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/models/charitable_organization_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CharitableOrganizationTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/ignored_amazon_product_need_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class IgnoredAmazonProductNeedTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/location_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LocationTest < ActiveSupport::TestCase 4 | # Location is not used directly but as a parent class. 5 | # Setup a child for testing 6 | class Child < Location 7 | config( 8 | organization: "sketch-city", 9 | legacy_table_name: "shelters" 10 | ) 11 | filter(:coordinates) 12 | filter(:limit) 13 | filter(:shelter) 14 | filter(:accepting, type: :truthy) 15 | legacy_field(:monkeys, legacy_column: "Big monkeys") 16 | legacy_field(:elephants, type: :boolean, legacy_column: "Max Elephants", display: false, updatable: false, admin_only: true) 17 | legacy_field(:tigers, type: :text, legacy_column: "Max Tigers", display: false, updatable: false) 18 | end 19 | class OtherChild < Location 20 | config( 21 | organization: "sketch-city", 22 | legacy_table_name: "rescues" 23 | ) 24 | end 25 | 26 | test "correct organization and legacy_table_name" do 27 | assert_equal("sketch-city", Child.organization, "Incorrect organization") 28 | assert_equal("Sketch City", Child.organization_display_name, "Incorrect organization display name") 29 | assert_equal("shelters", Child.legacy_table_name, "Incorrect legacy table name") 30 | assert_equal("Shelters", Child.legacy_table_display_name, "Incorrect legacy table display name") 31 | end 32 | test "sets filters correctly" do 33 | assert_equal(:coordinates, Child.filters[:coordinates].type, "Coordinates filter set improperly") 34 | assert_equal(:limit, Child.filters[:limit].type, "Limit filter set improperly") 35 | assert_equal(:string, Child.filters[:shelter].type, "String filter set improperly") 36 | assert_equal(:truthy, Child.filters[:accepting].type, "Boolean filter set improperly") 37 | end 38 | test "sets columns, header, updatable columns correctly" do 39 | assert_equal([:name, :address, :city, :state, :zip, :phone, :monkeys], Child.table_columns, "Columns set incorrectly") 40 | assert_equal(["Name", "Address", "City", "State", "Zip", "Phone", "Monkeys"], Child.table_headers, "Headers set incorrectly") 41 | assert_equal(["Name", "Address", "City", "State", "Zip", "Phone", "Big monkeys"], Child.legacy_headers, "Legacy headers set incorrectly") 42 | 43 | assert_equal([:elephants], Child.admin_columns, "Admin columns set incorrectly") 44 | assert_equal(["Elephants"], Child.admin_headers, "Admin headers set incorrectly") 45 | assert_equal(["Max Elephants"], Child.admin_legacy_headers, "Admin legacy headers set incorrectly") 46 | 47 | assert_equal([:name, :address, :city, :state, :zip, :phone, :monkeys], Child.update_fields.map(&:name), "Updatable fields set incorrectly") 48 | end 49 | test "sets legacy values on instance correctly" do 50 | child = Child.new(monkeys: 'bob') 51 | assert_equal(child.monkeys, "bob", "Strings for legacy data set incorrectly") 52 | 53 | [1, true, 'y'].each do |value| 54 | child = Child.new(elephants: value) 55 | assert_equal(true, child.elephants, "Booleans unable to process #{value}") 56 | end 57 | end 58 | test "default scope restricts results" do 59 | child = Child.create(monkeys: 'bob') 60 | 61 | assert_equal(0, OtherChild.count) 62 | assert_equal(1, Child.count) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /test/models/mucked_home_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MuckedHomeTest < ActiveSupport::TestCase 4 | 5 | test "MuckedHomes can be ordered by geo" do 6 | alan = mucked_homes(:alan) 7 | joe = mucked_homes(:joe) 8 | nearest_alan = MuckedHome.near([alan.latitude, alan.longitude], 100).first 9 | assert_equal nearest_alan, alan 10 | 11 | nearest_joe = MuckedHome.near([joe.latitude, joe.longitude], 100).first 12 | assert_equal nearest_joe, joe 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /test/models/need_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class NeedTest < ActiveSupport::TestCase 3 | 4 | test "the fixtures work" do 5 | assert_equal Need.count, 2 6 | end 7 | 8 | test "empty array if nothing" do 9 | need = Need.new 10 | array = need.clean_needs 11 | assert_equal 0, array.length 12 | end 13 | 14 | test "clean needs gets rid of opens at 6" do 15 | need = Need.new tell_us_about_the_supply_needs: "opens at 6,\n*perishable foods, \n*cleaning supplies" 16 | array = need.clean_needs 17 | assert_equal 2, array.length 18 | assert_equal "perishable foods", array.first 19 | assert_equal "cleaning supplies", array.last 20 | end 21 | 22 | test "clean needs gives nice array" do 23 | need = Need.new tell_us_about_the_supply_needs: "non-perishable foods, cleaning supplies" 24 | array = need.clean_needs 25 | assert_equal 2, array.length 26 | assert_equal "cleaning supplies", array.last 27 | end 28 | 29 | test "reports sufficient info for map when lat and lon present" do 30 | need = Need.new(latitude: 1, longitude: 1) 31 | 32 | assert_equal need.has_sufficient_data_for_map?, true 33 | end 34 | 35 | test "reports not sufficient info for map when lat or lon absent" do 36 | need = Need.new(latitude: nil, longitude: nil) 37 | 38 | assert_equal need.has_sufficient_data_for_map?, false 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/models/page_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageTest < ActiveSupport::TestCase 4 | 5 | test "key is required" do 6 | @page = Page.new 7 | @page.valid? 8 | assert @page.errors[:key].any? 9 | end 10 | 11 | test "key must be unique" do 12 | @page = Page.new 13 | @page.key = pages(:home).key 14 | @page.valid? 15 | assert @page.errors[:key].any? 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/models/shelter_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ShelterTest < ActiveSupport::TestCase 4 | 5 | test "the fixtures work" do 6 | assert_equal Shelter.count, 2 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/volunteer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class VolunteerTest < ActiveSupport::TestCase 4 | 5 | test "Volunteers can be ordered by geo" do 6 | jane = volunteers(:jane) 7 | john = volunteers(:john) 8 | nearest_jane = Volunteer.near([jane.latitude, jane.longitude], 100).first 9 | assert_equal nearest_jane, jane 10 | 11 | nearest_john = Volunteer.near([john.latitude, john.longitude], 100).first 12 | assert_equal nearest_john, john 13 | end 14 | 15 | 16 | end 17 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/test/system/.keep -------------------------------------------------------------------------------- /test/system/admin_searchings_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class AdminSearchingsTest < ApplicationSystemTestCase 4 | test "Filtering the shelters" do 5 | visit shelters_path 6 | fill_in "Search", with: shelters(:nrg).shelter 7 | assert_selector "#data-table", text: shelters(:nrg).shelter 8 | refute_selector "#data-table", text: shelters(:lonestar).shelter 9 | end 10 | 11 | test "Filtering the needs" do 12 | visit needs_path 13 | fill_in "Search", with: needs(:katy).location_name 14 | assert_selector "#data-table", text: needs(:katy).location_name 15 | refute_selector "#data-table", text: needs(:victory).location_name 16 | end 17 | 18 | test "Notifications of shelter updates" do 19 | visit shelters_path 20 | sleep 1 21 | rando_shelter = Shelter.all.sample 22 | rando_shelter.shelter = SecureRandom.hex(8) 23 | rando_shelter.save! 24 | assert_selector ".new-record-notification", text: "1 RECENT SHELTER UPDATES - RELOAD" 25 | end 26 | 27 | test "Notifications of need updates" do 28 | visit needs_path 29 | sleep 1 30 | rando_shelter = Need.all.sample 31 | rando_shelter.updated_by = SecureRandom.hex(8) 32 | rando_shelter.save! 33 | assert_selector ".new-record-notification", text: "1 RECENT NEED UPDATES - RELOAD" 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/system/homepage_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class HomepageTest < ApplicationSystemTestCase 4 | 5 | 6 | test "homepage can recover if no page exists" do 7 | pages(:home).destroy 8 | visit root_path 9 | assert_selector "article.page-home", text: "" 10 | end 11 | 12 | test "homepage has page content" do 13 | 14 | visit root_path 15 | assert_selector "article.page-home", text: pages(:home).content 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | require 'vcr' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | Geocoder.configure(lookup: :test) 11 | 12 | Geocoder::Lookup::Test.add_stub( 13 | [29.7488469, -95.4593152], [ 14 | { 15 | 'latitude' => 29.7496888, 16 | 'longitude' => -95.45794269999999, 17 | 'address' => '4800 Hallmark Dr, Houston, TX 77056, USA', 18 | 'state' => 'Texas', 19 | 'state_code' => 'TX', 20 | 'country' => 'United States', 21 | 'country_code' => 'US' 22 | } 23 | ] 24 | ) 25 | 26 | Rails.application.load_tasks 27 | end 28 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/tmp/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sketch-city/harvey-api/00b8d9ce005a69389b665cb66c9999f39fe046d8/vendor/.keep --------------------------------------------------------------------------------