├── log
└── .keep
├── storage
└── .keep
├── tmp
└── .keep
├── vendor
└── .keep
├── lib
├── assets
│ └── .keep
├── tasks
│ ├── .keep
│ └── scheduler.rake
├── templates
│ └── erb
│ │ └── scaffold
│ │ └── _form.html.erb
└── capistrano
│ └── tasks
│ └── envvars.rake
├── .ruby-gemset
├── .ruby-version
├── app
├── assets
│ ├── images
│ │ ├── .keep
│ │ ├── fogg.png
│ │ ├── fogg1.png
│ │ ├── gary_web.png
│ │ ├── abalone_logo.png
│ │ ├── icons
│ │ │ ├── plus.png
│ │ │ ├── plus.svg
│ │ │ └── pencil.svg
│ │ ├── Burgess white ab 1.png
│ │ └── abalone_upload_job.jpeg
│ ├── config
│ │ └── manifest.js
│ ├── stylesheets
│ │ ├── _utils.scss
│ │ ├── animals.scss
│ │ ├── pages
│ │ │ └── operations.scss
│ │ ├── file_uploads.scss
│ │ ├── _mixins.scss
│ │ ├── reports_kit.scss
│ │ └── _variables.scss
│ └── javascripts
│ │ └── application.js
├── models
│ ├── concerns
│ │ ├── .keep
│ │ ├── raw.rb
│ │ ├── organization_scope.rb
│ │ └── csv_exportable.rb
│ ├── current.rb
│ ├── operation_batch.rb
│ ├── application_record.rb
│ ├── animals_shl_number.rb
│ ├── shl_number.rb
│ ├── measurement_event.rb
│ ├── temporary_file.rb
│ ├── file_upload.rb
│ ├── exit_type.rb
│ ├── measurement_type.rb
│ ├── organization.rb
│ ├── location.rb
│ ├── user.rb
│ ├── ability.rb
│ ├── operation.rb
│ ├── enclosure.rb
│ ├── cohort.rb
│ ├── facility.rb
│ └── processed_file.rb
├── controllers
│ ├── concerns
│ │ └── .keep
│ ├── reports_controller.rb
│ ├── operations_controller.rb
│ ├── home_controller.rb
│ ├── cohort_imports_controller.rb
│ ├── passwords_controller.rb
│ ├── application_controller.rb
│ └── users_controller.rb
├── views
│ ├── layouts
│ │ ├── mailer.text.erb
│ │ └── mailer.html.erb
│ ├── facilities
│ │ ├── show.json.jbuilder
│ │ ├── index.json.jbuilder
│ │ ├── _facility.json.jbuilder
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ └── _form.html.erb
│ ├── devise
│ │ ├── mailer
│ │ │ ├── password_change.html.erb
│ │ │ ├── confirmation_instructions.html.erb
│ │ │ ├── unlock_instructions.html.erb
│ │ │ ├── email_changed.html.erb
│ │ │ └── reset_password_instructions.html.erb
│ │ ├── unlocks
│ │ │ └── new.html.erb
│ │ ├── confirmations
│ │ │ └── new.html.erb
│ │ ├── passwords
│ │ │ ├── edit.html.erb
│ │ │ └── new.html.erb
│ │ └── sessions
│ │ │ └── new.html.erb
│ ├── home
│ │ └── _footer.html.erb
│ ├── users
│ │ ├── shared
│ │ │ ├── _error_messages.html.erb
│ │ │ └── _links.html.erb
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ └── show.html.erb
│ ├── enclosures
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ ├── csv_upload.html.erb
│ │ └── _form.html.erb
│ ├── animals
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ └── csv_upload.html.erb
│ ├── cohorts
│ │ ├── new.html.erb
│ │ └── edit.html.erb
│ ├── measurement_types
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ ├── _form.html.erb
│ │ └── show.html.erb
│ ├── exit_types
│ │ ├── new.html.erb
│ │ ├── edit.html.erb
│ │ └── _form.html.erb
│ ├── locations
│ │ ├── edit.html.erb
│ │ ├── new.html.erb
│ │ └── _form.html.erb
│ ├── measurements
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ ├── cohort_imports
│ │ └── new.html.erb
│ ├── file_uploads
│ │ ├── upload.html.erb
│ │ ├── show_processing_csv_errors.html.erb
│ │ └── show.html.erb
│ ├── reports
│ │ └── index.html.erb
│ └── passwords
│ │ └── edit.html.erb
├── javascript
│ ├── packs
│ │ ├── tailwind.js
│ │ ├── styles.scss
│ │ ├── measurements.js
│ │ └── application.js
│ └── controllers
│ │ ├── navbar_controller.js
│ │ ├── index.js
│ │ ├── hello_controller.js
│ │ └── animals_controller.js
├── helpers
│ ├── facilities_helper.rb
│ ├── application_helper.rb
│ └── file_uploads_helper.rb
├── jobs
│ ├── application_job.rb
│ ├── measurement_job.rb
│ └── mortality_event_job.rb
├── mailers
│ └── application_mailer.rb
├── lib
│ ├── aggregates.rb
│ └── date_parser.rb
└── forms
│ └── csv_import_form.rb
├── .browserslistrc
├── public
├── apple-touch-icon.png
├── apple-touch-icon-precomposed.png
├── favicon.ico
├── images
│ └── abalone-hero.png
├── robots.txt
└── samples
│ ├── cohort.csv
│ ├── animal.csv
│ ├── enclosure.csv
│ ├── length_gonad_score_template.csv
│ └── animal_count_measurements_template.csv
├── .gitattributes
├── .rspec
├── spec
├── fixtures
│ ├── files
│ │ ├── tiny_custom_measurement.json
│ │ ├── enclosures.csv
│ │ ├── custom_measurements_multiple_models.csv
│ │ ├── animals.csv
│ │ ├── basic_custom_measurement_invalid.csv
│ │ ├── basic_custom_mortality_events.csv
│ │ ├── basic_custom_measurement.csv
│ │ └── basic_custom_measurement_with_spaces.csv
│ └── animals.csv
├── support
│ ├── csv
│ │ ├── invalid_headers.csv
│ │ └── basic_custom_measurement_invalid.csv
│ ├── factory_bot.rb
│ ├── file_upload_helpers.rb
│ └── shared_contexts
│ │ └── rake.rb
├── factories
│ ├── shl_numbers.rb
│ ├── temporary_files.rb
│ ├── operations.rb
│ ├── organizations.rb
│ ├── animals_shl_numbers.rb
│ ├── exit_types.rb
│ ├── operation_batches.rb
│ ├── measurement_events.rb
│ ├── measurement_types.rb
│ ├── enclosures.rb
│ ├── cohorts.rb
│ ├── mortality_events.rb
│ ├── blazer
│ │ └── queries.rb
│ ├── facility.rb
│ ├── file_upload.rb
│ ├── users.rb
│ ├── animals.rb
│ ├── locations.rb
│ ├── measurements.rb
│ └── processed_files.rb
├── controllers
│ ├── operations_controller_spec.rb
│ ├── reports_controller_spec.rb
│ └── blazer
│ │ └── queries_controller_spec.rb
├── models
│ ├── temporary_file_spec.rb
│ ├── operation_batch_spec.rb
│ ├── animals_shl_number_spec.rb
│ ├── shl_number_spec.rb
│ ├── organization_spec.rb
│ ├── measurement_event_spec.rb
│ ├── file_upload_spec.rb
│ ├── user_spec.rb
│ ├── enclosure_spec.rb
│ ├── location_spec.rb
│ ├── exit_type_spec.rb
│ └── processed_file_spec.rb
├── system
│ ├── reporting_spec.rb
│ ├── csv_upload_confirmation_spec.rb
│ ├── new_measurements_spec.rb
│ ├── download_csv_file_spec.rb
│ ├── locations
│ │ ├── index_spec.rb
│ │ ├── new_spec.rb
│ │ ├── update_spec.rb
│ │ ├── show_spec.rb
│ │ └── destroy_spec.rb
│ ├── facilities
│ │ ├── create_spec.rb
│ │ ├── destroy_spec.rb
│ │ ├── show_spec.rb
│ │ └── update_spec.rb
│ ├── cohorts
│ │ ├── delete_spec.rb
│ │ ├── create_spec.rb
│ │ ├── update_spec.rb
│ │ └── show_spec.rb
│ ├── animals
│ │ ├── create_spec.rb
│ │ ├── destroy_spec.rb
│ │ ├── upload_spec.rb
│ │ └── show_spec.rb
│ ├── exit_types
│ │ ├── show_spec.rb
│ │ ├── destroy_spec.rb
│ │ ├── create_spec.rb
│ │ └── index_spec.rb
│ ├── enclosures
│ │ ├── destroy_spec.rb
│ │ ├── show_spec.rb
│ │ ├── new_spec.rb
│ │ └── update_spec.rb
│ ├── csv_upload_pagination_spec.rb
│ ├── passwords
│ │ └── update_password_spec.rb
│ ├── measurement_types
│ │ ├── show_spec.rb
│ │ ├── new_spec.rb
│ │ └── destroy_spec.rb
│ ├── delete_failed_file_upload_spec.rb
│ └── users
│ │ ├── update_spec.rb
│ │ ├── delete_spec.rb
│ │ ├── new_spec.rb
│ │ └── show_spec.rb
├── jobs
│ └── measurement_job_spec.rb
└── requests
│ └── measurements_index_spec.rb
├── Procfile
├── db
├── migrate
│ ├── .DS_Store
│ ├── 20200922142446_add_role_to_users.rb
│ ├── 20200906162226_add_name_to_family.rb
│ ├── 20200922202022_drop_pedigrees.rb
│ ├── 20200924153501_add_cohort_id_to_animals.rb
│ ├── 20200923153522_drop_wild_collections.rb
│ ├── 20200924153007_add_location_id_to_enclosures.rb
│ ├── 20200922172429_drop_spawning_successes.rb
│ ├── 20200922185040_drop_mortality_tracking.rb
│ ├── 20200922161505_add_organization_to_tanks.rb
│ ├── 20200923124428_drop_population_estimates.rb
│ ├── 20191031181501_fix_column_typo.rb
│ ├── 20200405184451_add_measurement_date_to_measurements.rb
│ ├── 20200421024915_remove_measurements_from_tanks.rb
│ ├── 20200921205459_add_organization_to_families.rb
│ ├── 20200923152816_remove_tank_from_measurement_events.rb
│ ├── 20200924150252_remove_facility_id_from_enclosures.rb
│ ├── 20200922194114_add_unique_constraint_animals.rb
│ ├── 20200922195722_add_organization_to_operations.rb
│ ├── 20200922203142_drop_tagged_animal_assessments.rb
│ ├── 20200923130852_drop_consolidation_reports_table.rb
│ ├── 20200923154527_add_measurement_type_to_measurements.rb
│ ├── 20200921193217_add_organization_to_animals.rb
│ ├── 20200922182535_drop_untagged_animal_assessments.rb
│ ├── 20200922195812_add_organization_to_measurements.rb
│ ├── 20200923131001_add_unique_constraint_tanks.rb
│ ├── 20200923134705_drop_post_settlement_inventories.rb
│ ├── 20210930125236_add_exit_type_to_mortality_event.rb
│ ├── 20200618201524_add_processed_file_to_measurements.rb
│ ├── 20211010204649_add_organization_to_processed_file.rb
│ ├── 20200109184825_add_temporary_file_id_to_processed_files.rb
│ ├── 20200418143117_create_organizations.rb
│ ├── 20211008231933_add_organization_to_mortality_event.rb
│ ├── 20200204044451_remove_original_filename_from_processed_files.rb
│ ├── 20200418143934_add_org_indexes.rb
│ ├── 20211011181422_fix_versions_table.rb
│ ├── 20211014153539_add_processed_file_to_mortality_event.rb
│ ├── 20190726185815_change_spawning_successes_shl_number_to_string.rb
│ ├── 20200922195820_add_organization_to_measurement_events.rb
│ ├── 20200922213826_create_sex_enum.rb
│ ├── 20200924145521_create_shl_numbers.rb
│ ├── 20200405190459_add_dates_to_operations.rb
│ ├── 20200109183112_create_temporary_files.rb
│ ├── 20200506173804_add_tank_relationship_to_family.rb
│ ├── 20190726135958_create_facilities.rb
│ ├── 20190726191807_rename_spawing_successes_shl_number_to_shl_case_number.rb
│ ├── 20200506173350_add_family_relationship_to_operations.rb
│ ├── 20190726192306_rename_population_estimates_shl_number_to_shl_case_number.rb
│ ├── 20200203060633_change_population_estimate_abundace_field_type.rb
│ ├── 20200404224702_create_families.rb
│ ├── 20200404212335_create_tanks.rb
│ ├── 20211002202547_add_collected_to_animals.rb
│ ├── 20200923202015_change_tanks_to_enclosures.rb
│ ├── 20200924145950_create_animals_shl_numbers.rb
│ ├── 20210930124942_create_exit_types.rb
│ ├── 20211002202438_add_default_entry_point_to_animals.rb
│ ├── 20200923190307_change_families_to_cohort.rb
│ ├── 20200924210137_rework_animal_tags.rb
│ ├── 20200405182059_create_operations.rb
│ ├── 20200405181908_create_measurements.rb
│ ├── 20200905154358_add_animal_family_tank_to_measurement.rb
│ ├── 20200922201229_create_file_uploads.rb
│ ├── 20200923150158_create_measurement_types.rb
│ ├── 20200924151343_create_locations.rb
│ ├── 20201020103647_create_mortality_events_table.rb
│ ├── 20200513172140_create_operation_batches.rb
│ ├── 20200404224902_create_consolidation_reports.rb
│ ├── 20200404222231_create_post_settlement_inventories.rb
│ ├── 20200404225209_create_animals.rb
│ ├── 20200420193620_create_measurement_events.rb
│ ├── 20190727173430_create_processed_files.rb
│ ├── 20190726184116_create_pedigrees.rb
│ ├── 20190726144739_create_spawning_success.rb
│ ├── 20190726183847_create_population_estimates.rb
│ ├── 20200922214542_migrate_animal_sex_to_postgres_enum.rb
│ ├── 20200923223021_add_object_changes_to_versions.rb
│ ├── 20200923151254_add_references_and_attributes_to_measurements.rb
│ ├── 20211002202239_update_animals_column_names.rb
│ ├── 20210213212906_create_active_storage_variant_records.active_storage.rb
│ ├── 20190727201619_add_processed_file_id_to_data.rb
│ ├── 20200506175333_rename_operations_operation_type_to_prevent_rails_naming_collisions.rb
│ ├── 20190726191528_create_untagged_animal_assessments.rb
│ ├── 20190726211013_change_wild_collection_field_types.rb
│ ├── 20190726182945_create_mortality_tracking.rb
│ ├── 20210213212905_add_service_name_to_active_storage_blobs.active_storage.rb
│ ├── 20190726191836_create_tagged_animal_assessments.rb
│ ├── 20190726182100_create_wild_collections.rb
│ ├── 20200922203301_create_active_storage_tables.active_storage.rb
│ └── 20200927001420_install_blazer.rb
└── sample_data_files
│ └── measurement
│ └── basic_measurement.csv
├── bin
├── dbinit
│ └── 001-create-databases.sql
├── rake
├── bundle
├── delayed_job
├── rails
├── webpack
├── webpack-dev-server
├── yarn
├── update
└── setup
├── config
├── initializers
│ ├── delayed_job.rb
│ ├── mime_types.rb
│ ├── filter_parameter_logging.rb
│ ├── application_controller_renderer.rb
│ ├── cookies_serializer.rb
│ ├── wrap_parameters.rb
│ ├── backtrace_silencers.rb
│ ├── assets.rb
│ └── inflections.rb
├── webpack
│ ├── environment.js
│ ├── test.js
│ ├── production.js
│ └── development.js
├── environment.rb
├── boot.rb
├── reports_kit
│ └── reports
│ │ └── total_animals_by_cohort.yml
├── credentials.yml.enc
├── locales
│ ├── simple_form.en.yml
│ └── en.yml
└── storage.yml
├── docker-entrypoint.sh
├── config.ru
├── Rakefile
├── .github
├── dependabot.yml
├── workflows
│ ├── rubocop.yml
│ └── brakeman.yml
└── FUNDING.yml
├── package.json
├── postcss.config.js
├── .gitignore
├── LICENSE.md
└── Dockerfile
/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/storage/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ruby-gemset:
--------------------------------------------------------------------------------
1 | -global
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.0.3
2 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | text=auto eol=lf
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/fixtures/files/tiny_custom_measurement.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/javascript/packs/tailwind.js:
--------------------------------------------------------------------------------
1 | import "./styles.scss"
2 |
--------------------------------------------------------------------------------
/app/helpers/facilities_helper.rb:
--------------------------------------------------------------------------------
1 | module FacilitiesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/spec/support/csv/invalid_headers.csv:
--------------------------------------------------------------------------------
1 | some,invalid,headers
2 | value,value1,value2
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bin/rails server -p $PORT -e $RAILS_ENV
2 | worker: bundle exec rake jobs:work
3 |
--------------------------------------------------------------------------------
/db/migrate/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/db/migrate/.DS_Store
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include Pagy::Frontend
3 | end
4 |
--------------------------------------------------------------------------------
/app/views/facilities/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.partial! "facilities/facility", facility: @facility
2 |
--------------------------------------------------------------------------------
/app/assets/images/fogg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/fogg.png
--------------------------------------------------------------------------------
/app/models/current.rb:
--------------------------------------------------------------------------------
1 | class Current < ActiveSupport::CurrentAttributes
2 | attribute :user
3 | end
4 |
--------------------------------------------------------------------------------
/bin/dbinit/001-create-databases.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE abalone_development;
2 | CREATE DATABASE abalone_test;
--------------------------------------------------------------------------------
/app/assets/images/fogg1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/fogg1.png
--------------------------------------------------------------------------------
/app/models/operation_batch.rb:
--------------------------------------------------------------------------------
1 | class OperationBatch < ApplicationRecord
2 | has_many :operations
3 | end
4 |
--------------------------------------------------------------------------------
/app/assets/images/gary_web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/gary_web.png
--------------------------------------------------------------------------------
/app/views/facilities/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array! @facilities, partial: "facilities/facility", as: :facility
2 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/public/images/abalone-hero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/public/images/abalone-hero.png
--------------------------------------------------------------------------------
/spec/support/factory_bot.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.include FactoryBot::Syntax::Methods
3 | end
4 |
--------------------------------------------------------------------------------
/app/assets/images/abalone_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/abalone_logo.png
--------------------------------------------------------------------------------
/app/assets/images/icons/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/icons/plus.png
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/config/initializers/delayed_job.rb:
--------------------------------------------------------------------------------
1 | Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
2 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | module.exports = environment
4 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/spec/factories/shl_numbers.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :shl_number do
3 | animals_shl_number
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/temporary_files.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :temporary_file do
3 | contents { "" }
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/assets/images/Burgess white ab 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/Burgess white ab 1.png
--------------------------------------------------------------------------------
/app/assets/images/abalone_upload_job.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubyforgood/abalone/HEAD/app/assets/images/abalone_upload_job.jpeg
--------------------------------------------------------------------------------
/app/javascript/packs/styles.scss:
--------------------------------------------------------------------------------
1 | @import "tailwindcss/base";
2 | @import "tailwindcss/components";
3 | @import "tailwindcss/utilities";
--------------------------------------------------------------------------------
/spec/factories/operations.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :operation do
3 | enclosure
4 | organization
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/app/models/animals_shl_number.rb:
--------------------------------------------------------------------------------
1 | class AnimalsShlNumber < ApplicationRecord
2 | belongs_to :animal
3 | belongs_to :shl_number
4 | end
5 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/spec/controllers/operations_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe OperationsController, type: :controller do
4 | end
5 |
--------------------------------------------------------------------------------
/spec/factories/organizations.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :organization do
3 | name { Faker::Name.unique.name }
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/spec/factories/animals_shl_numbers.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :animals_shl_number do
3 | animal
4 | shl_number
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/factories/exit_types.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :exit_type do
3 | name { 'Incidental' }
4 | organization
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/models/shl_number.rb:
--------------------------------------------------------------------------------
1 | class ShlNumber < ApplicationRecord
2 | has_many :animals_shl_numbers
3 | has_many :animals, through: :animals_shl_numbers
4 | end
5 |
--------------------------------------------------------------------------------
/spec/factories/operation_batches.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :operation_batch do
3 | sequence(:name) { |n| "Operation #{n}" }
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/views/facilities/_facility.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! facility, :id, :name, :code, :created_at, :updated_at
2 | json.url facility_url(facility, format: :json)
3 |
--------------------------------------------------------------------------------
/spec/factories/measurement_events.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :measurement_event do
3 | name { "MyString" }
4 | organization
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_utils.scss:
--------------------------------------------------------------------------------
1 | // Breakpoints for responsiveness
2 | $mobile: 480px;
3 | $tablet: 768px;
4 | $desktop-min: $tablet + 1px;
5 | $desktop-max: 1024px;
6 |
--------------------------------------------------------------------------------
/app/controllers/reports_controller.rb:
--------------------------------------------------------------------------------
1 | class ReportsController < ApplicationController
2 | # def index; end
3 | end
4 | #
5 | # Replaced by blazer reporting - 1/24/21
6 |
--------------------------------------------------------------------------------
/app/models/measurement_event.rb:
--------------------------------------------------------------------------------
1 | class MeasurementEvent < ApplicationRecord
2 | include OrganizationScope
3 |
4 | has_many :measurements, dependent: :destroy
5 | end
6 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | if [ "$1" = 'abalone' ]; then
5 | exec su-exec app bundle exec rails s -b 0.0.0.0
6 | else
7 | exec "$@"
8 | fi
9 |
--------------------------------------------------------------------------------
/spec/models/temporary_file_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe TemporaryFile, type: :model do
4 | it { should have_one(:processed_file) }
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/temporary_file.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class TemporaryFile < ApplicationRecord
4 | has_one :processed_file, inverse_of: :temporary_file
5 | end
6 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/password_change.html.erb:
--------------------------------------------------------------------------------
1 |
We're contacting you to notify you that your password has been changed.
4 |
--------------------------------------------------------------------------------
/spec/models/operation_batch_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe OperationBatch, type: :model do
4 | it { is_expected.to have_many(:operations) }
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922142446_add_role_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddRoleToUsers < ActiveRecord::Migration[6.0]
2 | def change
3 | add_column :users, :role, :integer
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/measurement_types.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :measurement_type do
3 | name { "length" }
4 | unit { "cm" }
5 | organization
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200906162226_add_name_to_family.rb:
--------------------------------------------------------------------------------
1 | class AddNameToFamily < ActiveRecord::Migration[5.2]
2 | def change
3 | add_column :families, :name, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922202022_drop_pedigrees.rb:
--------------------------------------------------------------------------------
1 | class DropPedigrees < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :pedigrees
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/factories/enclosures.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :enclosure do
3 | sequence(:name) { |n| "Enclosure #{n}" }
4 | location
5 | organization
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/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 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/db/migrate/20200924153501_add_cohort_id_to_animals.rb:
--------------------------------------------------------------------------------
1 | class AddCohortIdToAnimals < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :animals, :cohort, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/db/migrate/20200923153522_drop_wild_collections.rb:
--------------------------------------------------------------------------------
1 | class DropWildCollections < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :wild_collections
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200924153007_add_location_id_to_enclosures.rb:
--------------------------------------------------------------------------------
1 | class AddLocationIdToEnclosures < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :enclosures, :location
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/concerns/raw.rb:
--------------------------------------------------------------------------------
1 | module Raw
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | scope :raw, -> { where(raw: true) }
6 | scope :not_raw, -> { where(raw: false) }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/models/file_upload.rb:
--------------------------------------------------------------------------------
1 | class FileUpload < ApplicationRecord
2 | include OrganizationScope
3 |
4 | belongs_to :user
5 | has_one_attached :file
6 |
7 | validates_presence_of :status, :user
8 | end
9 |
--------------------------------------------------------------------------------
/bin/delayed_job:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
4 | require 'delayed/command'
5 | Delayed::Command.new(ARGV).daemonize
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922172429_drop_spawning_successes.rb:
--------------------------------------------------------------------------------
1 | class DropSpawningSuccesses < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :spawning_successes
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200922185040_drop_mortality_tracking.rb:
--------------------------------------------------------------------------------
1 | class DropMortalityTracking < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :mortality_trackings
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/app/models/exit_type.rb:
--------------------------------------------------------------------------------
1 | class ExitType < ApplicationRecord
2 | include OrganizationScope
3 |
4 | has_many :mortality_events, dependent: :restrict_with_error
5 |
6 | validates :name, presence: true
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200922161505_add_organization_to_tanks.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToTanks < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :tanks, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200923124428_drop_population_estimates.rb:
--------------------------------------------------------------------------------
1 | class DropPopulationEstimates < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :population_estimates
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/factories/cohorts.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :cohort do
3 | female factory: :female
4 | male factory: :male
5 | organization
6 | name { Faker::Name.unique.name }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20191031181501_fix_column_typo.rb:
--------------------------------------------------------------------------------
1 | class FixColumnTypo < ActiveRecord::Migration[5.2]
2 | def change
3 | rename_column :wild_collections, :collection_coodinates, :collection_coordinates
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/views/home/_footer.html.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/db/migrate/20200405184451_add_measurement_date_to_measurements.rb:
--------------------------------------------------------------------------------
1 | class AddMeasurementDateToMeasurements < ActiveRecord::Migration[5.2]
2 | def change
3 | add_column :measurements, :date, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200421024915_remove_measurements_from_tanks.rb:
--------------------------------------------------------------------------------
1 | class RemoveMeasurementsFromTanks < ActiveRecord::Migration[5.2]
2 | def change
3 | remove_reference :measurements, :tank, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200921205459_add_organization_to_families.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToFamilies < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :families, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200923152816_remove_tank_from_measurement_events.rb:
--------------------------------------------------------------------------------
1 | class RemoveTankFromMeasurementEvents < ActiveRecord::Migration[6.0]
2 | def change
3 | remove_column :measurement_events, :tank_id
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200924150252_remove_facility_id_from_enclosures.rb:
--------------------------------------------------------------------------------
1 | class RemoveFacilityIdFromEnclosures < ActiveRecord::Migration[6.0]
2 | def change
3 | remove_column :enclosures, :facility_id, :integer
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/fixtures/files/enclosures.csv:
--------------------------------------------------------------------------------
1 | facility_code,location_name,name
2 | AOP,SeaLab2020,Enclosure Number 1
3 | AOP,Atlantis,Enclosure Number 2
4 | AOP,SeaWorld,Enclosure Number 2
5 | QQQ,OceanPlace,Enclosure Number 1
6 | ,,
7 |
--------------------------------------------------------------------------------
/app/models/measurement_type.rb:
--------------------------------------------------------------------------------
1 | class MeasurementType < ApplicationRecord
2 | include OrganizationScope
3 |
4 | has_many :measurements, dependent: :restrict_with_error
5 |
6 | validates :name, :unit, presence: true
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200922194114_add_unique_constraint_animals.rb:
--------------------------------------------------------------------------------
1 | class AddUniqueConstraintAnimals < ActiveRecord::Migration[6.0]
2 | def change
3 | add_index :animals, [:pii_tag, :organization_id], unique: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922195722_add_organization_to_operations.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToOperations < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :operations, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922203142_drop_tagged_animal_assessments.rb:
--------------------------------------------------------------------------------
1 | class DropTaggedAnimalAssessments < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :tagged_animal_assessments
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200923130852_drop_consolidation_reports_table.rb:
--------------------------------------------------------------------------------
1 | class DropConsolidationReportsTable < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :consolidation_reports
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200923154527_add_measurement_type_to_measurements.rb:
--------------------------------------------------------------------------------
1 | class AddMeasurementTypeToMeasurements < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :measurements, :measurement_type
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/fixtures/files/custom_measurements_multiple_models.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type,value,measurement_event,enclosure_name,cohort_name,tag,reason
2 | 9/01/21,Cohort,count,12,Monthly Count,Test Enclosure,New Cohort,
3 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/animals.scss:
--------------------------------------------------------------------------------
1 | #animal_collected_true,
2 | #animal_collected_false {
3 | margin-right: 0.25rem;
4 | }
5 |
6 | .animals__collected-fieldset {
7 | label {
8 | margin-right: 2rem;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/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 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/db/migrate/20200921193217_add_organization_to_animals.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToAnimals < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :animals, :organization, foreign_key: true, after: :sex
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922182535_drop_untagged_animal_assessments.rb:
--------------------------------------------------------------------------------
1 | class DropUntaggedAnimalAssessments < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :untagged_animal_assessments
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20200922195812_add_organization_to_measurements.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToMeasurements < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :measurements, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200923131001_add_unique_constraint_tanks.rb:
--------------------------------------------------------------------------------
1 | class AddUniqueConstraintTanks < ActiveRecord::Migration[6.0]
2 | def change
3 | add_index :tanks, [:name, :facility_id, :organization_id], unique: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200923134705_drop_post_settlement_inventories.rb:
--------------------------------------------------------------------------------
1 | class DropPostSettlementInventories < ActiveRecord::Migration[6.0]
2 | def up
3 | drop_table :post_settlement_inventories
4 | end
5 |
6 | def down; end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20210930125236_add_exit_type_to_mortality_event.rb:
--------------------------------------------------------------------------------
1 | class AddExitTypeToMortalityEvent < ActiveRecord::Migration[6.1]
2 | def change
3 | add_reference :mortality_events, :exit_type, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200618201524_add_processed_file_to_measurements.rb:
--------------------------------------------------------------------------------
1 | class AddProcessedFileToMeasurements < ActiveRecord::Migration[5.2]
2 | def change
3 | add_reference :measurements, :processed_file, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20211010204649_add_organization_to_processed_file.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToProcessedFile < ActiveRecord::Migration[6.1]
2 | def change
3 | add_reference :processed_files, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200109184825_add_temporary_file_id_to_processed_files.rb:
--------------------------------------------------------------------------------
1 | class AddTemporaryFileIdToProcessedFiles < ActiveRecord::Migration[5.2]
2 | def change
3 | add_column :processed_files, :temporary_file_id, :integer
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200418143117_create_organizations.rb:
--------------------------------------------------------------------------------
1 | class CreateOrganizations < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :organizations do |t|
4 | t.string :name
5 | t.timestamps
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20211008231933_add_organization_to_mortality_event.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToMortalityEvent < ActiveRecord::Migration[6.1]
2 | def change
3 | add_reference :mortality_events, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/factories/mortality_events.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :mortality_event do
3 | cohort
4 | animal
5 | organization
6 |
7 | trait :for_cohort do
8 | animal { nil }
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/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 |
6 | Mime::Type.register "text/csv", :csv
7 |
--------------------------------------------------------------------------------
/db/migrate/20200204044451_remove_original_filename_from_processed_files.rb:
--------------------------------------------------------------------------------
1 | class RemoveOriginalFilenameFromProcessedFiles < ActiveRecord::Migration[5.2]
2 | def change
3 | remove_column :processed_files, :original_filename
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200418143934_add_org_indexes.rb:
--------------------------------------------------------------------------------
1 | class AddOrgIndexes < ActiveRecord::Migration[5.2]
2 | def change
3 | add_reference :users, :organization, index: true
4 | add_reference :facilities, :organization, index: true
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20211011181422_fix_versions_table.rb:
--------------------------------------------------------------------------------
1 | class FixVersionsTable < ActiveRecord::Migration[6.1]
2 | def change
3 | remove_column :versions, "{:null=>false}"
4 |
5 | change_column_null :versions, :item_type, false
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20211014153539_add_processed_file_to_mortality_event.rb:
--------------------------------------------------------------------------------
1 | class AddProcessedFileToMortalityEvent < ActiveRecord::Migration[6.1]
2 | def change
3 | add_reference :mortality_events, :processed_file, foreign_key: true
4 | end
5 | end
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 |
--------------------------------------------------------------------------------
/db/migrate/20190726185815_change_spawning_successes_shl_number_to_string.rb:
--------------------------------------------------------------------------------
1 | class ChangeSpawningSuccessesShlNumberToString < ActiveRecord::Migration[5.2]
2 | def change
3 | change_column :spawning_successes, :shl_number, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922195820_add_organization_to_measurement_events.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationToMeasurementEvents < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :measurement_events, :organization, foreign_key: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200922213826_create_sex_enum.rb:
--------------------------------------------------------------------------------
1 | class CreateSexEnum < ActiveRecord::Migration[6.0]
2 | def up
3 | create_enum :animal_sex, %w[unknown male female]
4 | end
5 |
6 | def down
7 | drop_enum :animal_sex
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20200924145521_create_shl_numbers.rb:
--------------------------------------------------------------------------------
1 | class CreateShlNumbers < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :shl_numbers do |t|
4 | t.string :code
5 |
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/factories/blazer/queries.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :blazer_query, class: Blazer::Query do
3 | name { 'Save the snails!' }
4 | statement { "SELECT * FROM animals" }
5 | creator { FactoryBot.create(:creator) }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/db/migrate/20200405190459_add_dates_to_operations.rb:
--------------------------------------------------------------------------------
1 | class AddDatesToOperations < ActiveRecord::Migration[5.2]
2 | def change
3 | add_column :operations, :operation_date, :datetime
4 | add_column :operations, :operation_type, :string
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20200109183112_create_temporary_files.rb:
--------------------------------------------------------------------------------
1 | class CreateTemporaryFiles < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :temporary_files do |t|
4 | t.text :contents
5 |
6 | t.timestamps
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 | <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
6 |
--------------------------------------------------------------------------------
/db/migrate/20200506173804_add_tank_relationship_to_family.rb:
--------------------------------------------------------------------------------
1 | class AddTankRelationshipToFamily < ActiveRecord::Migration[5.2]
2 | def change
3 | change_table :families do |table|
4 | table.references :tank, index: true, null: true
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20190726135958_create_facilities.rb:
--------------------------------------------------------------------------------
1 | class CreateFacilities < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :facilities do |t|
4 | t.string :name
5 | t.string :code
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20190726191807_rename_spawing_successes_shl_number_to_shl_case_number.rb:
--------------------------------------------------------------------------------
1 | class RenameSpawingSuccessesShlNumberToShlCaseNumber < ActiveRecord::Migration[5.2]
2 | def change
3 | rename_column :spawning_successes, :shl_number, :shl_case_number
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200506173350_add_family_relationship_to_operations.rb:
--------------------------------------------------------------------------------
1 | class AddFamilyRelationshipToOperations < ActiveRecord::Migration[5.2]
2 | def change
3 | change_table :operations do |table|
4 | table.references :family, index: true
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/models/animals_shl_number_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe AnimalsShlNumber, type: :model do
4 | it "Animals SHL Numbers has associations" do
5 | is_expected.to belong_to(:animal)
6 | is_expected.to belong_to(:shl_number)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20190726192306_rename_population_estimates_shl_number_to_shl_case_number.rb:
--------------------------------------------------------------------------------
1 | class RenamePopulationEstimatesShlNumberToShlCaseNumber < ActiveRecord::Migration[5.2]
2 | def change
3 | rename_column :population_estimates, :shl_number, :shl_case_number
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200203060633_change_population_estimate_abundace_field_type.rb:
--------------------------------------------------------------------------------
1 | class ChangePopulationEstimateAbundaceFieldType < ActiveRecord::Migration[5.2]
2 | def change
3 | change_column :population_estimates, :abundance, "integer USING abundance::integer"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20200404224702_create_families.rb:
--------------------------------------------------------------------------------
1 | class CreateFamilies < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :families do |t|
4 | t.references :female
5 | t.references :male
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/public/samples/cohort.csv:
--------------------------------------------------------------------------------
1 | name,female_tag,male_tag,enclosure
2 | Cabrillo Marine Aquarium location enclosure cohort,Green_389,Blue_125,Cabrillo Marine Aquarium location enclosure
3 | The Abalone Farm location enclosure cohort,Green_578,Red_430,The Abalone Farm location enclosure
4 | ,,,
--------------------------------------------------------------------------------
/db/migrate/20200404212335_create_tanks.rb:
--------------------------------------------------------------------------------
1 | class CreateTanks < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :tanks do |t|
4 | t.references :facility, foreign_key: true
5 | t.string :name
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/public/samples/animal.csv:
--------------------------------------------------------------------------------
1 | entry_year,entry_date,entry_point,tag,sex,collected
2 | 2020,2020-09-22T08:16:24-04:00,Collection Area 7,Green_389,female,true
3 | 2020,2020-09-22T10:13:01-08:00,,Red_562,female,false
4 | 2021,2021-10-11T12:20:11-03:00,Collection Area 2,Blue_123,male,true
5 | ,,,,,
--------------------------------------------------------------------------------
/spec/factories/facility.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :facility do
3 | val = Faker::Name.unique.name
4 | name { val }
5 | sequence :code do |idx|
6 | "#{val.gsub(/[aeiou|AEIOU|\s]+/, '').strip}-#{idx}"
7 | end
8 | organization
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/factories/file_upload.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :file_upload do
3 | user
4 | organization
5 | status { 'Pending' }
6 | file { Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec', 'fixtures', 'files', 'animals.csv'), 'text/csv') }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/spec/models/shl_number_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe ShlNumber, type: :model do
4 | it "SHL Number has associations" do
5 | is_expected.to have_many(:animals_shl_numbers)
6 | is_expected.to have_many(:animals).through(:animals_shl_numbers)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 | if ENV['RAILS_ENV'] == 'test'
6 | require 'simplecov'
7 | SimpleCov.start 'rails'
8 | puts "required simplecov"
9 | end
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/pages/operations.scss:
--------------------------------------------------------------------------------
1 | @import "variables";
2 | @import "mixins";
3 |
4 | .operations__index{
5 | background: #f3f7fa;
6 | min-height: $body-height;
7 | padding: 2rem 4rem;
8 | &__container{
9 | background: white;
10 | min-height: 70vh;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/db/migrate/20211002202547_add_collected_to_animals.rb:
--------------------------------------------------------------------------------
1 | class AddCollectedToAnimals < ActiveRecord::Migration[6.1]
2 | def up
3 | add_column :animals, :collected, :boolean, default: false
4 | end
5 |
6 | def down
7 | remove_column :animals, :collected, :boolean
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/lib/aggregates.rb:
--------------------------------------------------------------------------------
1 | module Aggregates
2 | class Calculations
3 | # total egg, larval, or juvenile production by year (esp. how many year-old animals are produced annually)
4 | def self.offspring_production(_life_stage, _year)
5 | # TODO
6 | {}
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20200923202015_change_tanks_to_enclosures.rb:
--------------------------------------------------------------------------------
1 | class ChangeTanksToEnclosures < ActiveRecord::Migration[6.0]
2 | def change
3 | rename_table :tanks, :enclosures
4 | rename_column :cohorts, :tank_id, :enclosure_id
5 | rename_column :operations, :tank_id, :enclosure_id
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/public/samples/enclosure.csv:
--------------------------------------------------------------------------------
1 | facility_code,location_name,name
2 | AOP,Aquarium of the Pacific location,Aquarium of the Pacific location enclosure 1
3 | AOP,Aquarium of the Pacific location,Aquarium of the Pacific location enclosure 2
4 | CMA,Cabrillo Marine Aquarium location,Cabrillo Marine Aquarium location 1
5 | ,,
--------------------------------------------------------------------------------
/spec/factories/users.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :user do
3 | email { "#{SecureRandom.hex}@test.com" }
4 | password { "password" }
5 | password_confirmation { "password" }
6 | organization
7 | role { :user }
8 |
9 | trait(:admin) { role { :admin } }
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: bundler
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 |
9 | - package-ecosystem: "npm"
10 | directory: "/"
11 | schedule:
12 | interval: daily
13 | open-pull-requests-limit: 10
--------------------------------------------------------------------------------
/app/controllers/operations_controller.rb:
--------------------------------------------------------------------------------
1 | class OperationsController < ApplicationController
2 | def index
3 | @operations = Operation.for_organization(current_organization)
4 | .joins(:cohort, :enclosure)
5 | .includes(:cohort, :enclosure)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/config/reports_kit/reports/total_animals_by_cohort.yml:
--------------------------------------------------------------------------------
1 | # what we're counting/aggregating (y-axis)
2 | measure: animal
3 |
4 | contextual_filters:
5 | - for_organization
6 |
7 | filters:
8 | - created_at
9 | - cohort
10 |
11 | # columns/associations we're grouping by (x-axis)
12 | dimensions:
13 | - cohort
14 |
--------------------------------------------------------------------------------
/db/migrate/20200924145950_create_animals_shl_numbers.rb:
--------------------------------------------------------------------------------
1 | class CreateAnimalsShlNumbers < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :animals_shl_numbers do |t|
4 | t.references :animal
5 | t.references :shl_number
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20210930124942_create_exit_types.rb:
--------------------------------------------------------------------------------
1 | class CreateExitTypes < ActiveRecord::Migration[6.1]
2 | def change
3 | create_table :exit_types do |t|
4 | t.string :name
5 | t.belongs_to :organization, null: false, foreign_key: true
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20211002202438_add_default_entry_point_to_animals.rb:
--------------------------------------------------------------------------------
1 | class AddDefaultEntryPointToAnimals < ActiveRecord::Migration[6.1]
2 | def up
3 | change_column_default :animals, :entry_point, ''
4 | end
5 |
6 | def down
7 | change_column_default :animals, :entry_point, nil
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/fixtures/animals.csv:
--------------------------------------------------------------------------------
1 | entry_year,entry_date,entry_point,tag,sex,unknown_column,collected
2 | 2020,2020-09-22T08:16:24-04:00,sample,G001,female,unknown_data,false
3 | 2020,2020-09-22T08:16:24-04:00,sample,Y002,female,unknown_data,false
4 | 2020,2020-09-22T08:16:24-04:00,sample,Y002,female,unknown_data,false
5 |
--------------------------------------------------------------------------------
/spec/fixtures/files/animals.csv:
--------------------------------------------------------------------------------
1 | entry_year,entry_date,entry_point,tag,sex,unknown_column,collected
2 | 2020,2020-09-22T08:16:24-04:00,sample,G001,female,unknown_data,true
3 | 2020,2020-09-22T08:16:24-04:00,sample,Y002,female,unknown_data,true
4 | 2020,2020-09-22T08:16:24-04:00,sample,Y002,female,unknown_data,true
5 |
--------------------------------------------------------------------------------
/spec/fixtures/files/basic_custom_measurement_invalid.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type,value,measurement_event,enclosure_name,cohort_name,tag,reason
2 | 9/01/21,Cohort,count,,September Survey,Test Enclosure,Test Cohort,
3 | 9/01/21,Cohort,invalid measurement type,24,September Survey,Test Enclosure,Test Cohort,
4 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 |
5 | We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
5 | <% else %>
6 | We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/db/migrate/20200924151343_create_locations.rb:
--------------------------------------------------------------------------------
1 | class CreateLocations < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :locations do |t|
4 | t.string :name
5 | t.belongs_to :facility, null: false, foreign_key: true
6 | t.belongs_to :organization, null: false, foreign_key: true
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20201020103647_create_mortality_events_table.rb:
--------------------------------------------------------------------------------
1 | class CreateMortalityEventsTable < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :mortality_events do |t|
4 | t.datetime :mortality_date
5 | t.references :animal
6 | t.references :cohort
7 | t.integer :mortality_count
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20200513172140_create_operation_batches.rb:
--------------------------------------------------------------------------------
1 | class CreateOperationBatches < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :operation_batches do |t|
4 | t.string :name
5 | t.timestamps
6 | end
7 |
8 | change_table :operations do |operations_schema|
9 | operations_schema.references :operation_batch
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20200404224902_create_consolidation_reports.rb:
--------------------------------------------------------------------------------
1 | class CreateConsolidationReports < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :consolidation_reports do |t|
4 | t.references :family, foreign_key: true
5 | t.references :tank_from
6 | t.references :tank_to
7 | t.string :total_animal
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
--------------------------------------------------------------------------------
/spec/system/reporting_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'When I visit the blazer reporting index page', type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it 'I see the button to create a new query' do
11 | visit reports.root_path
12 | expect(page).to have_selector(:link_or_button, 'New Query')
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20200404222231_create_post_settlement_inventories.rb:
--------------------------------------------------------------------------------
1 | class CreatePostSettlementInventories < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :post_settlement_inventories do |t|
4 | t.datetime :inventory_date
5 | t.integer :mean_standard_length
6 | t.integer :total_per_tank
7 | t.references :tank, foreign_key: true
8 | end
9 | end
10 | end
11 |
12 |
--------------------------------------------------------------------------------
/db/migrate/20200404225209_create_animals.rb:
--------------------------------------------------------------------------------
1 | class CreateAnimals < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :animals do |t|
4 | t.integer :collection_year
5 | t.datetime :date_time_collected
6 | t.string :collection_position
7 | t.integer :pii_tag
8 | t.integer :tag_id
9 | t.string :sex
10 |
11 | t.timestamps
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/public/samples/length_gonad_score_template.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type,value,measurement_event,enclosure_name,cohort_name,tag,reason
2 | 9/01/21,Animal,length,14,September Survey,Aquarium of the Pacific location enclosure,Aquarium of the Pacific location enclosure cohort,F-AOP,,
3 | 9/01/21,Animal,gonad score,78,September Survey,Aquarium of the Pacific location enclosure,Aquarium of the Pacific location enclosure cohort,F-AOP,,
--------------------------------------------------------------------------------
/app/lib/date_parser.rb:
--------------------------------------------------------------------------------
1 | class DateParser
2 | # Returns the parsed date if it's in the mm/dd/yy format, and nil otherwise
3 | def self.parse(date)
4 | DateTime.strptime(date, "%m/%d/%y") if date_regex.match? date
5 | rescue ArgumentError
6 | nil
7 | end
8 |
9 | # regex to checking mm/dd/yy format for strptime above
10 | def self.date_regex
11 | Regexp.new('\d{1,2}\/\d{1,2}\/\d{2}\z').freeze
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20200420193620_create_measurement_events.rb:
--------------------------------------------------------------------------------
1 | class CreateMeasurementEvents < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :measurement_events do |t|
4 | t.string :name
5 | t.references :tank, foreign_key: true
6 |
7 | t.timestamps
8 | end
9 |
10 | change_table :measurements do |t|
11 | t.references :measurement_event, foreign_key: true
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/sample_data_files/measurement/basic_measurement.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type,value,measurement_event,enclosure_name,cohort_name,tag,reason
2 | 9/01/21,Enclosure,count,42,September Survey,California Science Center location enclosure,California Science Center location enclosure cohort
3 | 9/01/21,Animal,length,14,September Survey,California Science Center location enclosure,California Science Center location enclosure cohort,F-CSC
4 |
--------------------------------------------------------------------------------
/db/migrate/20190727173430_create_processed_files.rb:
--------------------------------------------------------------------------------
1 | class CreateProcessedFiles < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :processed_files do |t|
4 | t.string :filename
5 | t.string :original_filename
6 | t.string :category
7 | t.string :status
8 | t.jsonb :job_stats, null: false, default: '{}'
9 | t.text :job_errors
10 |
11 | t.timestamps
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/factories/animals.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :animal do
3 | entry_year { 1 }
4 | entry_date { "2020-04-04 18:52:09" }
5 | entry_point { "" }
6 | sequence(:tag) { |n| "G#{format('%03d', number: n)}" }
7 | sex { Animal.sexes.keys.sample }
8 | collected { false }
9 | organization
10 |
11 | factory(:male) { sex { 'male' } }
12 | factory(:female) { sex { 'female' } }
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/factories/locations.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :location do
3 | sequence(:name) { |n| "Location #{n}" }
4 | facility
5 | organization
6 |
7 | trait :with_enclosures do
8 | transient { enclosures_count { 1 } }
9 |
10 | after(:create) do |location, eval|
11 | create_list :enclosure, eval.enclosures_count, location: location
12 | location.reload
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/javascript/controllers/index.js:
--------------------------------------------------------------------------------
1 | // Load all the controllers within this directory and all subdirectories.
2 | // Controller files must be named *_controller.js.
3 |
4 | import { Application } from "stimulus"
5 | import { definitionsFromContext } from "stimulus/webpack-helpers"
6 |
7 | const application = Application.start()
8 | const context = require.context("controllers", true, /_controller\.js$/)
9 | application.load(definitionsFromContext(context))
10 |
--------------------------------------------------------------------------------
/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | skip_before_action :authenticate_user!
3 | def index
4 | return unless current_user
5 |
6 | # Retrive current organization stats
7 | @facility_count = current_user.organization.facilities.count
8 | @cohort_count = current_user.organization.cohorts.count
9 | @animal_count = current_user.organization.animals.count
10 | end
11 |
12 | def show; end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20190726184116_create_pedigrees.rb:
--------------------------------------------------------------------------------
1 | class CreatePedigrees < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :pedigrees do |t|
4 | t.boolean :raw, null: false, default: true
5 | t.string :cohort
6 | t.string :shl_case_number
7 | t.date :spawning_date
8 | t.string :mother
9 | t.string :father
10 | t.string :seperate_cross_within_cohort
11 |
12 | t.timestamps
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password. You can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | ljpRj4Zy8p8RHRt6IsV1Nxi/tXP6VUa59FPkiPzmt1hvyCR7V1syPn0maEOm8Dh9L8Z9o8/EwRu6N5w3xi61nTPPOnNpfrs5DvzfDGvdRT2HmHn5IWE464JALC9KiMxmKArIKyPYR17zYMeeMDhV/zIEgCLqaCtBa2sbMsO29JM9bG0MWmD3MWqHI3N6JMuR0AnynsuwuN4LGM4fyGZZke95c1fVLg8NLkmVaaTzhbJB/7rQ01S+5LLfDEpNdvESkCUVxQ04DVMhpuFlRFF8R5i+WKWeGH5hrVyc9rn0T/DOoS+wd3CNbRABlTNICWWnNG4n7bBg+ldE+/I/u1HlvLY/y6M15PXTnhGOojov4cFkRj+iTqHdUsOfPUAdvQsFjIFTO+tnyVT9pkgPKgTqk0Rp8+LMM4EoA4gA--u3F1Od+XLkMgV1ku--mBlOo7KruO1Yx9vOvbceAg==
--------------------------------------------------------------------------------
/app/models/concerns/csv_exportable.rb:
--------------------------------------------------------------------------------
1 | module CsvExportable
2 | extend ActiveSupport::Concern
3 |
4 | class_methods do
5 | def exportable_columns
6 | column_names.reject { |col| %w[organization_id].include? col }
7 | end
8 |
9 | def to_csv
10 | CSV.generate(headers: true) do |csv|
11 | csv << exportable_columns
12 | all.each { |record| csv << exportable_columns.map { |attr| record.send(attr) } }
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20190726144739_create_spawning_success.rb:
--------------------------------------------------------------------------------
1 | class CreateSpawningSuccess < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :spawning_successes do |t|
4 | t.boolean :raw, null: false, default: true
5 | t.string :tag
6 | t.numeric :shl_number
7 | t.date :spawning_date
8 | t.date :date_attempted
9 | t.string :spawning_success
10 | t.numeric :nbr_of_eggs_spawned
11 |
12 | t.timestamps
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/webpack_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::WebpackRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/views/users/shared/_error_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if resource.errors.any? %>
2 |
3 |
4 | <%= I18n.t("errors.messages.not_saved",
5 | count: resource.errors.count,
6 | resource: resource.class.model_name.human.downcase)
7 | %>
8 |
9 |
10 | <% resource.errors.full_messages.each do |message| %>
11 | <%= message %>
12 | <% end %>
13 |
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/app/javascript/packs/measurements.js:
--------------------------------------------------------------------------------
1 | document.addEventListener("DOMContentLoaded",function(){
2 | let typeSelect = document.querySelector('#data_import_type')
3 | updateDataImportDownload(typeSelect.value)
4 |
5 | typeSelect.addEventListener('change', (e) => {
6 | updateDataImportDownload(e.target.value)
7 | })
8 | });
9 |
10 | const updateDataImportDownload = (val) => {
11 | document.querySelector('#data-import-template-download')
12 | .href=`/samples/${val}.csv`
13 | }
14 |
--------------------------------------------------------------------------------
/db/migrate/20190726183847_create_population_estimates.rb:
--------------------------------------------------------------------------------
1 | class CreatePopulationEstimates < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :population_estimates do |t|
4 | t.boolean :raw, null: false, default: true
5 | t.date :sample_date
6 | t.string :shl_number
7 | t.date :spawning_date
8 | t.string :lifestage
9 | t.string :abundance
10 | t.string :facility
11 | t.string :notes
12 |
13 | t.timestamps
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20200922214542_migrate_animal_sex_to_postgres_enum.rb:
--------------------------------------------------------------------------------
1 | class MigrateAnimalSexToPostgresEnum < ActiveRecord::Migration[6.0]
2 | def up
3 | execute <<-DDL
4 | ALTER TABLE animals ALTER COLUMN sex TYPE animal_sex
5 | USING CASE sex
6 | WHEN 'male' THEN 'male'::animal_sex
7 | WHEN 'female' THEN 'female'::animal_sex
8 | WHEN NULL THEN 'unknown'::animal_sex
9 | END;
10 | ALTER TABLE animals ALTER COLUMN sex SET NOT NULL
11 | DDL
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "bundler/setup"
11 |
12 | require "webpacker"
13 | require "webpacker/dev_server_runner"
14 |
15 | APP_ROOT = File.expand_path("..", __dir__)
16 | Dir.chdir(APP_ROOT) do
17 | Webpacker::DevServerRunner.run(ARGV)
18 | end
19 |
--------------------------------------------------------------------------------
/app/controllers/cohort_imports_controller.rb:
--------------------------------------------------------------------------------
1 | class CohortImportsController < ApplicationController
2 | def new
3 | @cohort_import_form = CsvImportForm.new
4 | end
5 |
6 | def create
7 | @cohort_import_form = CsvImportForm.new csv_file: params.dig(:csv_import_form, :csv_file)
8 |
9 | if @cohort_import_form.save(user: current_user, organization: current_organization)
10 | redirect_to cohorts_path, notice: 'Processing file...'
11 | else
12 | render :new
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ApplicationRecord
2 | include OrganizationScope
3 |
4 | enum role: %i[user admin]
5 | after_initialize :set_default_role, if: :new_record?
6 |
7 | def set_default_role
8 | self.role ||= :user
9 | end
10 |
11 | # Include default devise modules. Others available are:
12 | # :confirmable, :lockable, :timeoutable, :trackable,
13 | # :registerable, and :omniauthable
14 | devise :database_authenticatable, :recoverable,
15 | :rememberable, :validatable
16 | end
17 |
--------------------------------------------------------------------------------
/spec/controllers/reports_controller_spec.rb:
--------------------------------------------------------------------------------
1 | # Replaced by blazer reporting - 1/24/21
2 |
3 | # require 'rails_helper'
4 | #
5 | # describe ReportsController do
6 | # include Devise::Test::ControllerHelpers
7 | #
8 | # let(:user) { FactoryBot.create(:user) }
9 | #
10 | # before do
11 | # sign_in user
12 | # end
13 | #
14 | # describe '#index' do
15 | # it 'should have response code 200' do
16 | # get :index
17 | # expect(response.code).to eq '200'
18 | # end
19 | # end
20 | # end
21 |
--------------------------------------------------------------------------------
/spec/jobs/measurement_job_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe MeasurementJob do
4 | let(:filename) { "basic_measurement.csv" }
5 |
6 | it_behaves_like "import job" do
7 | let!(:organization) { create(:organization) }
8 | let!(:measurement_type) { create(:measurement_type, name: 'count', unit: 'number', organization_id: organization.id) }
9 | let!(:measurement_type2) { create(:measurement_type, name: 'length', unit: 'number', organization_id: organization.id) }
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/factories/measurements.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :measurement do
3 | value { "25" }
4 | measurement_type
5 | association :subject, factory: :animal
6 | measurement_event
7 | organization
8 |
9 | # Polymorphic associations for measurements
10 | trait(:for_cohort) { association :subject, factory: :cohort }
11 | trait(:for_enclosure) { association :subject, factory: :enclosure }
12 | trait(:for_animal) { association :subject, factory: :animal }
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/enclosures/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to enclosures_path, class: "inline-flex items-center py-2 mb-4 font-bold rounded text-primary-dark hover:text-primary" do %>
4 |
<< Cancel
5 | <% end %>
6 |
New Enclosure
7 |
8 | <%= render 'form', enclosure: @enclosure %>
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/spec/models/organization_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Organization, type: :model do
4 | it "Organization has associations" do
5 | is_expected.to have_many(:users)
6 | is_expected.to have_many(:facilities)
7 | is_expected.to have_many(:cohorts)
8 | is_expected.to have_many(:enclosures)
9 | is_expected.to have_many(:measurements)
10 | is_expected.to have_many(:measurement_events)
11 | is_expected.to have_many(:operations)
12 | is_expected.to have_many(:animals)
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/file_uploads.scss:
--------------------------------------------------------------------------------
1 | /*
2 | Place all the styles related to the matching controller here.
3 | They will automatically be included in application.css.
4 | */
5 |
6 | .container_with_scroll{
7 | overflow-x: scroll;
8 | }
9 | .column.is-thinner{
10 | padding: .15rem .75rem;
11 | }
12 |
13 | .has-text-success{
14 | color: hsl(141, 53%, 53%);
15 | }
16 |
17 | .has-text-danger{
18 | color: hsl(348, 100%, 61%);
19 | }
20 |
21 | .trash-ico{
22 | img {
23 | width: 1.125rem;
24 | margin: 0 auto;
25 | }
26 | }
--------------------------------------------------------------------------------
/app/views/users/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to users_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
New User
9 |
10 | <%= render 'form', user: @user %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/spec/support/csv/basic_custom_measurement_invalid.csv:
--------------------------------------------------------------------------------
1 | measurement_event,measurement,value,enclosure_name,animal_pii_tag,cohort_name,
2 | Michael Drinks the Water,Flavor,Salty,Support Rack 3,,,
3 | Michael Drinks the Water,Flavor,WAY too salty,AB-17,,,
4 | Michael Drinks the Water,Flavor,Good,,,,
5 | Michael Drinks the Correct Water,Flavor,Good,The Water Bottle at My Desk,,,
6 | Michael Drinks the Correct Water,Flavor,Excellent,Office Water Cooler,,,
7 | Michael last known test,Tanning Lotion Smell,Sort of like Sardine Oil,CB Husband Tanning Salon,,,
8 |
--------------------------------------------------------------------------------
/app/views/animals/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to animals_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
New Animal
9 |
10 | <%= render 'form', animal: @animal %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/cohorts/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to cohorts_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
New Cohort
9 |
10 | <%= render 'form', cohort: @cohort %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/enclosures/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to enclosures_path, class: "inline-flex items-center py-2 mb-4 font-bold rounded text-primary-dark hover:text-primary" do %>
4 |
<< Cancel
5 | <% end %>
6 |
Editing Enclosure
7 |
8 | <%= render 'form', enclosure: @enclosure %>
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/views/measurement_types/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
New Measurement Type
4 |
5 | <%= render 'form', measurement_type: @measurement_type %>
6 |
7 | <%= link_to '<< Back', measurement_types_path, class: 'text-primary-dark hover:text-primary font-bold py-2 rounded inline-flex items-center mb-4' %>
8 |
9 |
10 |
--------------------------------------------------------------------------------
/db/migrate/20200923223021_add_object_changes_to_versions.rb:
--------------------------------------------------------------------------------
1 | # This migration adds the optional `object_changes` column, in which PaperTrail
2 | # will store the `changes` diff for each update event. See the readme for
3 | # details.
4 | class AddObjectChangesToVersions < ActiveRecord::Migration[6.0]
5 | # The largest text column available in all supported RDBMS.
6 | # See `create_versions.rb` for details.
7 | TEXT_BYTES = 1_073_741_823
8 |
9 | def change
10 | add_column :versions, :object_changes, :text, limit: TEXT_BYTES
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/models/ability.rb:
--------------------------------------------------------------------------------
1 | class Ability
2 | # Add in CanCan's ability definition DSL
3 | include CanCan::Ability
4 |
5 | def initialize(user)
6 | return unless user.present?
7 |
8 | can %i[show destroy index], ProcessedFile, organization_id: user.organization_id
9 |
10 | [
11 | Animal,
12 | Cohort,
13 | Enclosure,
14 | ExitType,
15 | Facility,
16 | MeasurementType,
17 | User
18 | ].each do |model|
19 | can :manage, model, organization_id: user.organization_id
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/facilities/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to facilities_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
New Facility
9 |
10 | <%= render 'form', facility: @facility %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/measurement_types/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to '<< Back', measurement_types_path, class: 'text-primary-dark hover:text-primary font-bold py-2 rounded inline-flex items-center mb-4' %>
4 |
Editing Measurement Type
5 |
6 | <%= render 'form', measurement_type: @measurement_type %>
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/db/migrate/20200923151254_add_references_and_attributes_to_measurements.rb:
--------------------------------------------------------------------------------
1 | class AddReferencesAndAttributesToMeasurements < ActiveRecord::Migration[6.0]
2 | def change
3 | add_reference :measurements, :subject, polymorphic: true, null: false
4 | remove_column :measurements, :name
5 | remove_column :measurements, :value_type
6 | remove_column :measurements, :animal_id
7 | remove_column :measurements, :tank_id
8 | remove_column :measurements, :family_id
9 | change_column :measurements, :value, :string, null: false
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/javascript/controllers/hello_controller.js:
--------------------------------------------------------------------------------
1 | // Visit The Stimulus Handbook for more details
2 | // https://stimulusjs.org/handbook/introduction
3 | //
4 | // This example controller works with specially annotated HTML like:
5 | //
6 | //
7 | //
8 | //
9 |
10 | import { Controller } from "stimulus"
11 |
12 | export default class extends Controller {
13 | static targets = [ "output" ]
14 |
15 | connect() {
16 | this.outputTarget.textContent = 'Hello, Stimulus!'
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/views/exit_types/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to exit_types_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
New Exit Type
9 |
10 | <%= render 'form', exit_type: @exit_type %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/users/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to users_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing User
9 |
10 | <%= render 'form', user: @user %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/db/migrate/20211002202239_update_animals_column_names.rb:
--------------------------------------------------------------------------------
1 | class UpdateAnimalsColumnNames < ActiveRecord::Migration[6.1]
2 | def up
3 | rename_column :animals, :collection_year, :entry_year
4 | rename_column :animals, :date_time_collected, :entry_date
5 | rename_column :animals, :collection_position, :entry_point
6 | end
7 |
8 | def down
9 | rename_column :animals, :entry_year, :collection_year
10 | rename_column :animals, :entry_date, :date_time_collected
11 | rename_column :animals, :entry_point, :collection_position
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend unlock instructions
2 |
3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 |
6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
9 |
10 |
11 |
12 | <%= f.submit "Resend unlock instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "users/shared/links" %>
17 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/views/animals/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to animals_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing Animal
9 |
10 | <%= render 'form', animal: @animal %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/cohorts/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to cohorts_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing Cohort
9 |
10 | <%= render 'form', cohort: @cohort %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/facilities/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to facilities_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing Facility
9 |
10 | <%= render 'form', facility: @facility %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/exit_types/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to exit_types_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing Exit Type
9 |
10 | <%= render 'form', exit_type: @exit_type %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/locations/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to facility_locations_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing Location
9 |
10 | <%= render 'form', location: @location, facility: @facility %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/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| /my_noisy_library/.match?(line) }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
8 | # Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
9 |
--------------------------------------------------------------------------------
/lib/templates/erb/scaffold/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%# frozen_string_literal: true %>
2 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
3 | <%%= f.error_notification %>
4 | <%%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
5 |
6 |
7 | <%- attributes.each do |attribute| -%>
8 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
9 | <%- end -%>
10 |
11 |
12 |
13 | <%%= f.button :submit %>
14 |
15 | <%% end %>
16 |
--------------------------------------------------------------------------------
/app/views/locations/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to facility_locations_path(@facility) do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
New Location
9 |
10 | <%= render 'form', location: @location, facility: @facility %>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
5 | select { |dir| File.expand_path(dir) != __dir__ }.
6 | product(["yarn", "yarn.cmd", "yarn.ps1"]).
7 | map { |dir, file| File.expand_path(file, dir) }.
8 | find { |file| File.executable?(file) }
9 |
10 | if yarn
11 | exec yarn, *ARGV
12 | else
13 | $stderr.puts "Yarn executable was not detected in the system."
14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
15 | exit 1
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/fixtures/files/basic_custom_measurement.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type,value,measurement_event,enclosure_name,cohort_name,tag,reason
2 | 9/01/21,Cohort,count,24,September Survey,Test Enclosure,Test Cohort,
3 | 9/01/21,Enclosure,count,42,September Survey,Test Enclosure,Test Cohort,
4 | 9/01/21,Animal,length,14,September Survey,Test Enclosure,Test Cohort,F-AOP,
5 | 9/01/21,Animal,gonad score,78,September Survey,Test Enclosure,Test Cohort,F-AOP,
6 | 9/01/21,Animal,length,16,September Survey,Test Enclosure,Test Cohort,M-AOP,
7 | 9/01/21,Animal,gonad score,46,September Survey,Test Enclosure,Test Cohort,M-AOP,
8 |
--------------------------------------------------------------------------------
/spec/system/csv_upload_confirmation_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "file confirm queue page", type: :system do
4 | let(:user) { create(:user) }
5 | let(:valid_file) { "#{Rails.root}/db/sample_data_files/measurement/basic_measurement.csv" }
6 |
7 | context "after uploading a file" do
8 | it "should have a link to view all files" do
9 | sign_in user
10 | visit new_file_upload_path
11 | upload_file("Measurement", [valid_file])
12 | click_link('View All Files')
13 | expect(page).to have_current_path(file_uploads_path)
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_mixins.scss:
--------------------------------------------------------------------------------
1 | @import './utils.scss';
2 |
3 | //Small Devices
4 | @mixin mobile {
5 | @media (max-width: $mobile) {
6 | @content;
7 | }
8 | }
9 |
10 | //Medium (tablets)
11 | @mixin tablet {
12 | @media (max-width: $tablet) {
13 | @content;
14 | }
15 | }
16 |
17 | //Small laptops and desktops
18 | @mixin mediaLg {
19 | @media (max-width: $desktop-max) and (min-width: $tablet+1) {
20 | @content;
21 | }
22 | }
23 |
24 | //Large devices
25 | @mixin mediaXL {
26 | @media (min-width: $desktop-max) and (min-width: $desktop-min) {
27 | @content;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/views/measurements/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= link_to measurements_path do %>
4 |
5 | << Cancel
6 |
7 | <% end %>
8 |
Editing Measurement
9 |
10 | <%= render 'form', measurement: @measurement %>
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/models/measurement_event_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe MeasurementEvent, type: :model do
4 | subject(:measurement_event) { build_stubbed(:measurement_event) }
5 |
6 | it "has associations" do
7 | is_expected.to have_many(:measurements)
8 | is_expected.to belong_to(:organization)
9 | end
10 |
11 | describe "Validations >" do
12 | subject(:measurement_event) { build(:measurement_event) }
13 |
14 | it "has a valid factory" do
15 | expect(measurement_event).to be_valid
16 | end
17 |
18 | include_examples OrganizationScope
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/models/file_upload_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe FileUpload do
4 | subject(:file_upload) { build_stubbed(:file_upload) }
5 |
6 | it "has associations" do
7 | is_expected.to belong_to(:user)
8 | end
9 |
10 | describe "Validations >" do
11 | subject(:file_upload) { build(:file_upload) }
12 |
13 | it "has a valid factory" do
14 | expect(file_upload).to be_valid
15 | end
16 |
17 | include_examples OrganizationScope
18 |
19 | it { is_expected.to validate_presence_of(:status) }
20 | it { is_expected.to validate_presence_of(:user) }
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/system/new_measurements_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'When I visit the New Measurements page', type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | visit new_measurement_path
9 | end
10 |
11 | it 'Then I can generate a template for my selected measurements' do
12 | expect(page).to have_selector('label.sr-only')
13 | expect(page).to have_content 'I am taking measurements of animals'
14 | expect(page).to have_content 'I want to measure length and gonad score'
15 | expect(page).to have_link('Generate Template')
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/db/migrate/20210213212906_create_active_storage_variant_records.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20191206030411)
2 | class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
3 | def change
4 | create_table :active_storage_variant_records do |t|
5 | t.belongs_to :blob, null: false, index: false
6 | t.string :variation_digest, null: false
7 |
8 | t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
9 | t.foreign_key :active_storage_blobs, column: :blob_id
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/system/download_csv_file_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "download CSV file from upload files", type: :system do
4 | include Devise::Test::IntegrationHelpers
5 |
6 | let(:user) { create(:user) }
7 |
8 | it "When I click on previous file name I download csv file" do
9 | file = FactoryBot.create(:processed_file, organization: user.organization)
10 | sign_in user
11 | visit file_uploads_path
12 | click_on(file.filename)
13 | filename = file.filename.gsub('(', '%28').gsub(')', '%29')
14 | expect(page.response_headers['Content-Disposition']).to include(filename)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20190727201619_add_processed_file_id_to_data.rb:
--------------------------------------------------------------------------------
1 | class AddProcessedFileIdToData < ActiveRecord::Migration[5.2]
2 | def change
3 | add_column :spawning_successes, :processed_file_id, :integer
4 | add_column :wild_collections, :processed_file_id, :integer
5 | add_column :mortality_trackings, :processed_file_id, :integer
6 | add_column :population_estimates, :processed_file_id, :integer
7 | add_column :pedigrees, :processed_file_id, :integer
8 | add_column :untagged_animal_assessments, :processed_file_id, :integer
9 | add_column :tagged_animal_assessments, :processed_file_id, :integer
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20200506175333_rename_operations_operation_type_to_prevent_rails_naming_collisions.rb:
--------------------------------------------------------------------------------
1 | class RenameOperationsOperationTypeToPreventRailsNamingCollisions < ActiveRecord::Migration[5.2]
2 | def change
3 | # `type` is a reserved word in Rails, that can cause
4 | # surprising behavior when using Single Table Iheritance
5 | # or Polymorphic assocations.
6 |
7 | # This is because Rails infers that fields named with `type`
8 | # indicate the class that should be used when deserializing
9 | # the data from the database into a Rails model.
10 | rename_column :operations, :operation_type, :action
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/.github/workflows/rubocop.yml:
--------------------------------------------------------------------------------
1 | name: rubocop
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - 'docs/**'
9 | - '*.md'
10 | - 'bin/*'
11 | pull_request:
12 | branches:
13 | - main
14 | paths-ignore:
15 | - 'docs/**'
16 | - '*.md'
17 | - 'bin/*'
18 |
19 | jobs:
20 | ruby_lint:
21 |
22 | runs-on: ubuntu-latest
23 |
24 | steps:
25 | - uses: actions/checkout@v2
26 |
27 | - name: Set up Ruby
28 | uses: ruby/setup-ruby@v1
29 | with:
30 | bundler-cache: true
31 |
32 | - name: Run Rubocop
33 | run: bundle exec rubocop
--------------------------------------------------------------------------------
/app/assets/images/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/spec/system/locations/index_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the locations index page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see a list of locations" do
11 | facility = create(:facility)
12 | locations = create_list(:location, 3, facility: facility, organization: user.organization)
13 |
14 | visit facility_locations_path(facility)
15 |
16 | locations.each do |location|
17 | expect(page).to have_content(location.name)
18 | expect(page).to have_content(location.facility_name)
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe User, type: :model do
4 | let!(:user) { create(:user) }
5 |
6 | describe "Validations >" do
7 | subject(:user) { build(:user) }
8 |
9 | it "has a valid factory" do
10 | expect(user).to be_valid
11 | end
12 |
13 | it_behaves_like OrganizationScope
14 |
15 | it { is_expected.to validate_presence_of(:email) }
16 | it { is_expected.to validate_presence_of(:password) }
17 | end
18 |
19 | describe "Callbacks >" do
20 | it "initializes with a default role of 'user'" do
21 | expect(User.new.role).to eq("user")
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/db/migrate/20190726191528_create_untagged_animal_assessments.rb:
--------------------------------------------------------------------------------
1 | class CreateUntaggedAnimalAssessments < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :untagged_animal_assessments do |t|
4 | t.boolean :raw, null: false, default: true
5 | t.date :measurement_date
6 | t.string :cohort
7 | t.date :spawning_date
8 | t.numeric :growout_rack
9 | t.string :growout_column
10 | t.numeric :growout_trough
11 | t.numeric :length
12 | t.numeric :mass
13 | t.string :gonad_score
14 | t.string :predicted_sex
15 | t.text :notes
16 |
17 | t.timestamps
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/.github/workflows/brakeman.yml:
--------------------------------------------------------------------------------
1 | name: brakeman
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths-ignore:
8 | - 'docs/**'
9 | - '*.md'
10 | - 'bin/*'
11 | pull_request:
12 | branches:
13 | - main
14 | paths-ignore:
15 | - 'docs/**'
16 | - '*.md'
17 | - 'bin/*'
18 |
19 | jobs:
20 | brakeman:
21 |
22 | runs-on: ubuntu-latest
23 |
24 | steps:
25 | - uses: actions/checkout@v2
26 |
27 | - name: Set up Ruby
28 | uses: ruby/setup-ruby@v1
29 | with:
30 | bundler-cache: true
31 |
32 | - name: Run Brakeman
33 | run: bundle exec brakeman -Aw1 --no-pager
--------------------------------------------------------------------------------
/app/helpers/file_uploads_helper.rb:
--------------------------------------------------------------------------------
1 | module FileUploadsHelper
2 | def file_upload_status_style(status)
3 | if success? status
4 | 'fa fa-check has-text-success'
5 | elsif pending? status
6 | 'fas fa-spinner'
7 | elsif failed? status
8 | 'fas fa-exclamation-triangle has-text-danger'
9 | end
10 | end
11 |
12 | def success?(status)
13 | status.include?('0 records had errors') || status == 'Processed'
14 | end
15 |
16 | def pending?(status)
17 | status.include?('Pending') || status == 'Running'
18 | end
19 |
20 | def failed?(status)
21 | status == 'Failed' || status.match(/[1-9]\d* records had errors/)
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/forms/csv_import_form.rb:
--------------------------------------------------------------------------------
1 | class CsvImportForm
2 | include ActiveModel::Model
3 |
4 | attr_accessor :csv_file
5 |
6 | validates :csv_file, presence: true
7 |
8 | def save(user:, organization:)
9 | valid_form? && ImportCohortsJob.perform_later(
10 | FileUpload.create(user: user, organization: organization, status: 'Pending', file: csv_file)
11 | )
12 | end
13 |
14 | private
15 |
16 | def valid_form?
17 | valid? && valid_content_type?
18 | end
19 |
20 | def valid_content_type?
21 | return true if csv_file&.content_type == 'text/csv'
22 |
23 | errors.add :csv_file, 'Invalid file type. Please upload a CSV.'
24 | false
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend confirmation instructions
2 |
3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 |
6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
9 |
10 |
11 |
12 | <%= f.submit "Resend confirmation instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "users/shared/links" %>
17 |
--------------------------------------------------------------------------------
/db/migrate/20190726211013_change_wild_collection_field_types.rb:
--------------------------------------------------------------------------------
1 | class ChangeWildCollectionFieldTypes < ActiveRecord::Migration[5.2]
2 | def change
3 | change_column :wild_collections, :collection_depth, "numeric USING collection_depth::numeric"
4 | change_column :wild_collections, :length, "numeric USING collection_depth::numeric"
5 | change_column :wild_collections, :weight, "numeric USING collection_depth::numeric"
6 | change_column :wild_collections, :otc_treatment_completion_date, "date USING otc_treatment_completion_date::date"
7 | rename_column :wild_collections, :final_holding_facility_date_of_arrival, :final_holding_facility_and_date_of_arrival
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [rubyforgood]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/reports_kit.scss:
--------------------------------------------------------------------------------
1 | // Replaced by blazer reporting - 1/24/21
2 |
3 | @import "variables";
4 | @import "mixins";
5 |
6 | .abalone-chart {
7 | position: relative;
8 | }
9 |
10 | .reports_kit_report {
11 | form {
12 | select.select2, span.select2 {
13 | display: inline-block;
14 | width: 200px;
15 | }
16 | .select2-search {
17 | input {
18 | height: 2.1em !important;
19 | }
20 | }
21 | }
22 | }
23 |
24 | .reports_kit_visualization {
25 | width: 100%;
26 | }
27 |
28 | /*
29 | ** stick download buttons to upper-right of chart
30 | */
31 | .reports_kit_actions {
32 | position: absolute;
33 | top: 0;
34 | right: 0;
35 | }
36 |
--------------------------------------------------------------------------------
/spec/controllers/blazer/queries_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Blazer::QueriesController, type: :controller do
4 | routes { Blazer::Engine.routes }
5 |
6 | let(:user) { FactoryBot.create(:user) }
7 |
8 | before { sign_in(user) }
9 |
10 | describe 'index' do
11 | before do
12 | FactoryBot.create_list(:blazer_query, 3, creator: user)
13 | FactoryBot.create_list(:blazer_query, 7, creator: FactoryBot.create(:user))
14 | end
15 |
16 | it 'returns queries scoped to current_user\'s organization' do
17 | get :index
18 | queries = JSON.parse(response.body)
19 | expect(queries.count).to eq(3)
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/db/migrate/20190726182945_create_mortality_tracking.rb:
--------------------------------------------------------------------------------
1 | class CreateMortalityTracking < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :mortality_trackings do |t|
4 | t.boolean :raw, default: true, null: false
5 | t.date :mortality_date
6 | t.string :cohort
7 | t.string :shl_case_number
8 | t.date :spawning_date
9 | t.integer :shell_box
10 | t.string :shell_container
11 | t.string :animal_location
12 | t.integer :number_morts
13 | t.string :approximation
14 | t.string :processed_by_shl
15 | t.string :initials
16 | t.string :tags
17 | t.string :comments
18 |
19 | t.timestamps
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/models/operation.rb:
--------------------------------------------------------------------------------
1 | class Operation < ApplicationRecord
2 | include OrganizationScope
3 |
4 | belongs_to :enclosure
5 |
6 | belongs_to :operation_batch, required: false
7 | belongs_to :cohort, required: false
8 |
9 | validates :cohort, presence: true, if: :add_cohort?
10 |
11 | def perform
12 | case action.to_sym
13 | when :remove_cohort
14 | enclosure.update(cohort: nil)
15 | when :add_cohort
16 | enclosure.update(cohort: cohort)
17 | else
18 | raise InvalidActionError, action
19 | end
20 | end
21 |
22 | private
23 |
24 | def add_cohort?
25 | action&.to_sym == :add_cohort
26 | end
27 |
28 | class InvalidActionError < StandardError; end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/fixtures/files/basic_custom_measurement_with_spaces.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type , value, measurement_event ,enclosure_name,cohort_name,tag, reason
2 | 9/01/21, Cohort, count, 24,September Survey, Test Enclosure, Test Cohort,
3 | 9/01/21,Enclosure,count , 42,September Survey,Test Enclosure, Test Cohort,
4 | 9/01/21, Animal , length, 14,September Survey,Test Enclosure,Test Cohort,F-AOP,
5 | 9/01/21,Animal,gonad score ,78, September Survey, Test Enclosure,Test Cohort , F-AOP,
6 | 9/01/21,Animal,length,16,September Survey, Test Enclosure,Test Cohort,M-AOP,
7 | 9/01/21 ,Animal, gonad score , 46,September Survey,Test Enclosure,Test Cohort, M-AOP,
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/db/migrate/20210213212905_add_service_name_to_active_storage_blobs.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20190112182829)
2 | class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
3 | def up
4 | unless column_exists?(:active_storage_blobs, :service_name)
5 | add_column :active_storage_blobs, :service_name, :string
6 |
7 | if configured_service = ActiveStorage::Blob.service.name
8 | ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
9 | end
10 |
11 | change_column :active_storage_blobs, :service_name, :string, null: false
12 | end
13 | end
14 |
15 | def down
16 | remove_column :active_storage_blobs, :service_name
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/public/samples/animal_count_measurements_template.csv:
--------------------------------------------------------------------------------
1 | date,subject_type,measurement_type,value,measurement_event,enclosure_name,cohort_name,tag,reason
2 | 9/01/21,Cohort,count,24,September Survey,Aquarium of the Pacific location enclosure,Aquarium of the Pacific location enclosure cohort,,
3 | 9/01/21,Enclosure,count,42,September Survey,Aquarium of the Pacific location enclosure,Aquarium of the Pacific location enclosure cohort,,
4 | 9/01/21,Animal,animal mortality event,,September Survey,Aquarium of the Pacific location enclosure,Aquarium of the Pacific location enclosure cohort,Green_289,sacrifice
5 | 9/01/21,Cohort,cohort mortality event,5,September Survey,Aquarium of the Pacific location enclosure,Aquarium of the Pacific location enclosure cohort,,natural causes
6 |
--------------------------------------------------------------------------------
/spec/system/facilities/create_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the facility New page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "And fill out the form and click the submit button, a facility is created" do
11 | visit new_facility_path
12 |
13 | within('form') do
14 | fill_in 'Name', with: 'Aquarium of the Pacific'
15 | fill_in 'Code', with: 'AOP'
16 | click_on 'Submit'
17 | end
18 |
19 | expect(page).to have_content 'Facility was successfully created.'
20 | within('.container') do
21 | expect(page).to have_content 'Aquarium of the Pacific'
22 | expect(page).to have_content 'AOP'
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/system/cohorts/delete_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the cohort Index page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, cohort should be deleted" do
11 | cohort1 = create(:cohort, organization: user.organization)
12 | cohort_count = Cohort.for_organization(user.organization).count
13 |
14 | visit cohorts_path
15 |
16 | link = find("a[data-method='delete'][href='#{cohort_path(cohort1.id)}']")
17 |
18 | within('tbody') do
19 | expect(page).to have_xpath('.//tr', count: cohort_count)
20 | link.click
21 | expect(page).to have_xpath('.//tr', count: (cohort_count - 1))
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/system/locations/new_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the location New page", type: :system do
4 | let(:user) { create(:user) }
5 | let!(:facility) { create(:facility, organization_id: user.organization_id) }
6 |
7 | before do
8 | sign_in user
9 | end
10 |
11 | it "Then I see the location form" do
12 | visit new_facility_location_path(facility)
13 |
14 | expect(page).to have_content("New Location")
15 | expect(page).to have_content("Name")
16 | end
17 |
18 | it "I can create a new location" do
19 | visit new_facility_location_path(facility)
20 |
21 | fill_in('Name', with: "Secret location")
22 | click_button('Submit')
23 |
24 | expect(page).to have_content("Secret location")
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/db/migrate/20190726191836_create_tagged_animal_assessments.rb:
--------------------------------------------------------------------------------
1 | class CreateTaggedAnimalAssessments < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :tagged_animal_assessments do |t|
4 | t.boolean :raw, null: false, default: true
5 | t.date :measurement_date
6 | t.string :shl_case_number
7 | t.date :spawning_date
8 | t.string :tag
9 | t.string :from_growout_rack
10 | t.string :from_growout_column
11 | t.string :from_growout_trough
12 | t.string :to_growout_rack
13 | t.string :to_growout_column
14 | t.string :to_growout_trough
15 | t.numeric :length
16 | t.string :gonad_score
17 | t.string :predicted_sex
18 | t.text :notes
19 |
20 | t.timestamps
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/system/animals/create_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the animal New page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "And fill out the form and click the submit button, animal should be created" do
11 | cohort = create(:cohort, organization: user.organization)
12 |
13 | visit new_animal_path
14 |
15 | within('form') do
16 | fill_in 'animal_tag', with: "G127"
17 | select "Male", from: 'animal_sex'
18 | select cohort.name, from: 'animal_cohort_id'
19 | fill_in 'animal_shl_numbers_codes', with: "c123,k123-32"
20 | click_on 'Submit'
21 | end
22 |
23 | expect(page).to have_content 'Animal was successfully created.'
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/system/exit_types/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the exit type Show page", type: :system do
4 | it "I see information of a specific exit type" do
5 | user = create(:user, :admin)
6 | exit_type = create(:exit_type, name: "test", organization: user.organization)
7 |
8 | sign_in user
9 |
10 | visit exit_type_path(exit_type)
11 |
12 | expect(page).to have_content(exit_type.name)
13 | end
14 |
15 | it "I can't see an exit type of another organization" do
16 | user = create(:user, :admin)
17 | exit_type = create(:exit_type, name: "test")
18 |
19 | sign_in user
20 |
21 | visit exit_type_path(exit_type)
22 |
23 | expect(page).to have_content("You are not authorized to access this resource.")
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/controllers/passwords_controller.rb:
--------------------------------------------------------------------------------
1 | class PasswordsController < ApplicationController
2 | before_action :set_user
3 | before_action :current_user?
4 |
5 | def edit; end
6 |
7 | def update
8 | if @user.update(user_params)
9 | bypass_sign_in(@user)
10 | redirect_to edit_password_url, notice: 'Password was successfully updated.'
11 | else
12 | render :edit
13 | end
14 | end
15 |
16 | private
17 |
18 | def user_params
19 | params.require(:user).permit(
20 | :password
21 | ).merge(organization_id: current_organization.id)
22 | end
23 |
24 | def set_user
25 | @user = User.find(params[:id])
26 | end
27 |
28 | def current_user?
29 | redirect_to root_path, alert: "Not authorized" unless current_user == @user
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/system/facilities/destroy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the facility Index page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, the facility is deleted" do
11 | facility = create(:facility, organization: user.organization)
12 | facility_count = Facility.for_organization(user.organization).count
13 |
14 | visit facilities_path
15 |
16 | link = find("a[data-method='delete'][href='#{facility_path(facility.id)}']")
17 |
18 | within('tbody') do
19 | expect(page).to have_xpath('.//tr', count: facility_count)
20 | link.click
21 | expect(page).to have_xpath('.//tr', count: (facility_count - 1))
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/system/animals/destroy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the animal Index page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, animal should be deleted" do
11 | animal1 = create(:animal, entry_year: 2, tag: "G123", organization: user.organization)
12 | animal_count = Animal.for_organization(user.organization).count
13 |
14 | visit animals_path
15 |
16 | link = find("a[data-method='delete'][href='#{animal_path(animal1.id)}']")
17 |
18 | within('tbody') do
19 | expect(page).to have_xpath('.//tr', count: animal_count)
20 | link.click
21 | expect(page).to have_xpath('.//tr', count: (animal_count - 1))
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/system/locations/update_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the location Edit page", type: :system do
4 | let(:user) { create(:user) }
5 | let!(:facility) { create(:facility, organization_id: user.organization_id) }
6 |
7 | before do
8 | sign_in user
9 | end
10 |
11 | it "And fill out the form and click the submit button, location should be updated" do
12 | location = create(:location, facility: facility)
13 |
14 | visit edit_facility_location_path(facility, location)
15 |
16 | within('form') do
17 | fill_in 'location_name', with: "Public location"
18 | click_on 'Submit'
19 | end
20 |
21 | expect(page).to have_content 'Location was successfully updated.'
22 | expect(page).to have_content "Public location"
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/system/enclosures/destroy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the enclosure Index page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, enclosure should be deleted" do
11 | enclosure1 = create(:enclosure, organization: user.organization)
12 | enclosure_count = Enclosure.for_organization(user.organization).count
13 |
14 | visit enclosures_path
15 |
16 | link = find("a[data-method='delete'][href='#{enclosure_path(enclosure1.id)}']")
17 |
18 | within('tbody') do
19 | expect(page).to have_xpath('.//tr', count: enclosure_count)
20 | link.click
21 | expect(page).to have_xpath('.//tr', count: (enclosure_count - 1))
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/system/facilities/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the facility Show page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "I see information of a specific facility" do
11 | facility = create(:facility, organization: user.organization)
12 |
13 | visit facility_path(facility)
14 |
15 | within('.container') do
16 | expect(page).to have_content facility.name
17 | expect(page).to have_content facility.code
18 | end
19 | end
20 |
21 | it "I can't see a facility of another organization" do
22 | facility = create(:facility)
23 |
24 | visit facility_path(facility)
25 |
26 | expect(page).to have_content("You are not authorized to access this resource.")
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/system/enclosures/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the enclosure Show page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see information of a specific enclosure" do
11 | enclosure = FactoryBot.create(:enclosure, organization: user.organization)
12 |
13 | visit enclosure_path(enclosure)
14 |
15 | expect(page).to have_content(enclosure.name)
16 | expect(page).to have_content(enclosure.location.name)
17 | end
18 |
19 | it "I can't see an enclosure of another organization" do
20 | enclosure = FactoryBot.create(:enclosure)
21 |
22 | visit enclosure_path(enclosure)
23 |
24 | expect(page).to have_content("You are not authorized to access this resource.")
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/models/enclosure.rb:
--------------------------------------------------------------------------------
1 | class Enclosure < ApplicationRecord
2 | has_paper_trail
3 |
4 | include OrganizationScope
5 | include CsvExportable
6 |
7 | belongs_to :location, optional: true
8 | has_many :operations, dependent: :destroy
9 | has_many :measurements, as: :subject
10 |
11 | has_one :cohort, required: false
12 |
13 | validates :name, uniqueness: { scope: %i[organization_id location_id] }
14 |
15 | delegate :name, to: :location, prefix: true, allow_nil: true
16 | delegate :facility_name, to: :location, allow_nil: true
17 | delegate :facility_code, to: :location, allow_nil: true
18 |
19 | def self.exportable_columns
20 | column_names.reject { |col| %w[organization_id facility_id].include? col }.insert(2, 'facility_name')
21 | end
22 |
23 | def empty?
24 | cohort.blank?
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/views/users/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
Organization
10 | <%= @user.organization.name %>
11 |
Email
12 | <%= @user.email %>
13 |
14 |
15 | <%= link_to '<< BACK', users_path, class: "mr-3 mb-3 button-outline-primary" %>
16 | <%= link_to "Edit", edit_user_path(@user), class: "button-outline-primary mt-4" %>
17 |
18 |
19 |
--------------------------------------------------------------------------------
/spec/system/exit_types/destroy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the exit type Index page", type: :system do
4 | let(:user) { create(:user, role: 'admin') }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, exit type should be deleted" do
11 | exit_type = FactoryBot.create(:exit_type, organization: user.organization)
12 | exit_type_count = ExitType.for_organization(user.organization).count
13 |
14 | visit exit_types_path
15 |
16 | link = find("a[data-method='delete'][href='#{exit_type_path(exit_type.id)}']")
17 |
18 | within('tbody') do
19 | expect(page).to have_xpath('.//tr', count: exit_type_count)
20 | link.click
21 | expect(page).to have_xpath('.//tr', count: (exit_type_count - 1))
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require_tree .
16 | //= require jquery3
17 | //= require reports_kit/application
18 |
--------------------------------------------------------------------------------
/spec/system/csv_upload_pagination_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "pagination for file uploads page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | context "when viewing the list of file uploads" do
7 | it "should paginate the list" do
8 | Pagy::DEFAULT[:items] = 3
9 | files = FactoryBot.create_list(:processed_file, 5, organization_id: user.organization_id)
10 | sign_in user
11 | visit file_uploads_path
12 | expect(page).to have_content(files.first.filename, count: 3)
13 | end
14 |
15 | it "should display page numbers" do
16 | Pagy::DEFAULT[:items] = 3
17 | FactoryBot.create_list(:processed_file, 5, organization_id: user.organization_id)
18 | sign_in user
19 | visit file_uploads_path
20 | expect(page).to have_link("2")
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/system/cohorts/create_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the cohort New page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "And fill out the form and click the submit button, cohort should be created" do
11 | female = create(:animal, sex: :female, tag: "G888", organization_id: user.organization_id)
12 | male = create(:animal, sex: :male, tag: "G987", organization_id: user.organization_id)
13 |
14 | visit new_cohort_path
15 |
16 | within('form') do
17 | fill_in 'cohort_name', with: "Gary's cohort"
18 | select female.tag, from: 'cohort_female_id'
19 | select male.tag, from: 'cohort_male_id'
20 | click_on 'Submit'
21 | end
22 |
23 | expect(page).to have_content 'Cohort was successfully created.'
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/system/exit_types/create_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the exit type New page", type: :system do
4 | let(:user) { create(:user, role: 'admin') }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "And fill out the form and click the submit button, exit type should be created" do
11 | visit new_exit_type_path
12 |
13 | within('form') do
14 | fill_in 'exit_type_name', with: "test"
15 | click_on 'Submit'
16 | end
17 |
18 | expect(page).to have_content 'Exit type was successfully created.'
19 | end
20 |
21 | it "And click the submit button without fill the name, an error message must be shown" do
22 | visit new_exit_type_path
23 |
24 | within('form') do
25 | click_on 'Submit'
26 | end
27 |
28 | expect(page).to have_content "Name can't be blank"
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/system/passwords/update_password_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the user Change password page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | context "As an regular user" do
11 | it "Then I can update my own the password" do
12 | visit edit_password_path(user)
13 |
14 | within('form') do
15 | fill_in('Password', with: "anewpassword")
16 | click_on('Submit')
17 | end
18 |
19 | expect(page).to have_content('Password was successfully updated.')
20 | end
21 |
22 | it "Then I should not authorized to change other user password" do
23 | another_user = create(:user)
24 |
25 | visit edit_password_path(another_user)
26 |
27 | expect(page).to have_content('Not authorized')
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/system/locations/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the location Show page" do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see information of a specific location" do
11 | facility = create(:facility)
12 | location = create(:location, facility: facility)
13 | enclosure1 = create(:enclosure, location: location)
14 | enclosure2 = create(:enclosure, location: location)
15 | enclosure3 = create(:enclosure, location: location)
16 |
17 | visit facility_location_path(facility, location)
18 |
19 | expect(page).to have_content(location.name)
20 | expect(page).to have_content(facility.name)
21 | expect(page).to have_content(enclosure1.name)
22 | expect(page).to have_content(enclosure2.name)
23 | expect(page).to have_content(enclosure3.name)
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/javascript/controllers/animals_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "stimulus"
2 |
3 | export default class extends Controller {
4 | connect() {
5 | this.displayEntryPoint()
6 | this.onInputChange()
7 | }
8 |
9 | displayEntryPoint() {
10 | document.querySelector('#animal_collected_true').checked
11 | ? this.showEntryPoint()
12 | : this.hideEntryPoint()
13 | }
14 |
15 | hideEntryPoint() {
16 | document.querySelector('.animals__entry-point').style.display = 'none';
17 | }
18 |
19 | showEntryPoint() {
20 | document.querySelector('.animals__entry-point').style.display = 'block';
21 | }
22 |
23 | onInputChange() {
24 | window.addEventListener('change', (event) => {
25 | if (event.target.value === 'true') {
26 | this.showEntryPoint();
27 | } else {
28 | this.hideEntryPoint();
29 | }
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/db/migrate/20190726182100_create_wild_collections.rb:
--------------------------------------------------------------------------------
1 | class CreateWildCollections < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :wild_collections do |t|
4 | t.boolean :raw, null: false, default: true
5 | t.string :tag
6 | t.date :collection_date
7 | t.string :general_location
8 | t.string :precise_location
9 | t.point :collection_coodinates
10 | t.string :proximity_to_nearest_neighbor
11 | t.string :collection_method_notes
12 | t.string :foot_condition_notes
13 | t.string :collection_depth
14 | t.string :length
15 | t.string :weight
16 | t.string :gonad_score
17 | t.string :predicted_sex
18 | t.string :initial_holding_facility
19 | t.string :final_holding_facility_date_of_arrival
20 | t.string :otc_treatment_completion_date
21 |
22 | t.timestamps
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/system/locations/destroy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the location Index page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, location should be deleted" do
11 | facility = create(:facility, organization: user.organization)
12 | location = create(:location, facility: facility, organization: user.organization)
13 | location_count = Location.for_organization(user.organization).count
14 |
15 | visit facility_locations_path(facility)
16 |
17 | link = find("a[data-method='delete'][href='#{facility_location_path(facility, location)}']")
18 |
19 | within('tbody') do
20 | expect(page).to have_xpath('.//tr', count: location_count)
21 | link.click
22 | expect(page).to have_xpath('.//tr', count: (location_count - 1))
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/system/measurement_types/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the measurement_type Show page", type: :system do
4 | let(:user) { create(:user, role: "admin") }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see information of a specific measurement_type" do
11 | measurement_type = FactoryBot.create(:measurement_type, organization: user.organization)
12 |
13 | visit measurement_type_path(measurement_type)
14 |
15 | expect(page).to have_content(measurement_type.name)
16 | expect(page).to have_content(measurement_type.unit)
17 | end
18 |
19 | it "I can't see a measurement_type of another organization" do
20 | measurement_type = FactoryBot.create(:measurement_type)
21 |
22 | visit measurement_type_path(measurement_type)
23 |
24 | expect(page).to have_content("You are not authorized to access this resource.")
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/views/animals/csv_upload.html.erb:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/spec/factories/processed_files.rb:
--------------------------------------------------------------------------------
1 | # rubocop:disable Lint/RedundantCopDisableDirective, Layout/LineLength
2 | # == Schema Information
3 | #
4 | # Table name: processed_files
5 | #
6 | # id :bigint not null, primary key
7 | # filename :string
8 | # original_filename :string
9 | # category :string
10 | # status :string
11 | # job_stats :jsonb not null
12 | # job_errors :text
13 | # created_at :datetime not null
14 | # updated_at :datetime not null
15 | #
16 | # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective
17 |
18 | FactoryBot.define do
19 | factory :processed_file do
20 | filename { "Measurement_12172018(original).csv" }
21 | category { "Measurement" }
22 | status { "Processed" }
23 | job_stats { "{}" }
24 | job_errors {}
25 | temporary_file
26 | organization
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/views/measurements/new.html.erb:
--------------------------------------------------------------------------------
1 |
18 |
19 | <%= javascript_pack_tag 'measurements', 'data-turbolinks-track': 'reload' %>
20 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/spec/system/cohorts/update_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the cohort Edit page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "And fill out the form and click the submit button, cohort should be updated" do
11 | cohort = create(:cohort, organization: user.organization)
12 |
13 | visit edit_cohort_path(cohort)
14 |
15 | within('form') do
16 | fill_in 'cohort_name', with: "Gary's new cohort"
17 | click_on 'Submit'
18 | end
19 |
20 | expect(page).to have_content 'Cohort was successfully updated.'
21 | expect(page).to have_content "Gary's new cohort"
22 | end
23 |
24 | it "I can't update a cohort of another organization" do
25 | cohort = create(:cohort)
26 |
27 | visit edit_cohort_path(cohort)
28 |
29 | expect(page).to have_content 'You are not authorized to access this resource.'
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/.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 | .tool-versions
16 | .env
17 |
18 | # Ignore uploaded files in development
19 | /storage/*
20 | !/storage/.keep
21 |
22 | /public/assets
23 | .byebug_history
24 |
25 | # Ignore master key for decrypting credentials and more.
26 | /config/master.key
27 | /config/local_env.yml
28 | .*.swp
29 | .idea/
30 |
31 | .DS_Store
32 | .vscode
33 | .generators
34 |
35 | /public/packs
36 | /public/packs-test
37 | /node_modules
38 | /yarn-error.log
39 | yarn-debug.log*
40 | .yarn-integrity
41 | /coverage
42 |
--------------------------------------------------------------------------------
/app/views/exit_types/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: exit_type, local: true, html: { class: "w-full max-w-md" }) do |form| %>
2 | <% if exit_type.errors.any? %>
3 |
4 |
<%= pluralize(exit_type.errors.count, "error") %> prohibited this exit type from being saved:
5 |
6 | <% exit_type.errors.full_messages.each do |message| %>
7 | <%= message %>
8 | <% end %>
9 |
10 |
11 | <% end %>
12 |
13 |
14 |
15 | <%= form.label :name, class: 'text-sm text-caption-light font-medium uppercase' %>
16 |
17 |
18 | <%= form.text_field :name, class: 'input mb-2' %>
19 |
20 |
21 |
22 |
23 |
24 | <%= form.submit('Submit', class: 'bg-primary-dark hover:bg-primary text-white font-bold py-2 px-4 rounded inline-flex items-center mt-4') %>
25 |
26 |
27 | <% end %>
28 |
--------------------------------------------------------------------------------
/spec/system/animals/upload_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the animal Upload Csv page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before { sign_in user }
7 |
8 | it "Then I can upload a CSV of animals to import" do
9 | visit csv_upload_animals_path
10 |
11 | expect(page).to have_link("Click here")
12 | expect(page).to have_content("Click here for a sample csv file.")
13 |
14 | attach_file('animal_csv', "#{Rails.root}/spec/fixtures/files/animals.csv")
15 |
16 | click_on 'Submit'
17 |
18 | expect(page).to have_current_path(animals_path)
19 |
20 | visit animals_path
21 |
22 | within('tbody') { expect(page).to have_xpath('.//tr', count: 2) }
23 |
24 | # Verify no duplicates and assigned to correct org
25 | animals = Animal.where(tag: "Y002")
26 | expect(animals.count).to eql(1)
27 | expect(animals.first.organization_id).to eql(user.organization.id)
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/views/locations/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: location, local: true, url: [facility, location], html: { class: "w-full max-w-md" }) do |form| %>
2 | <% if location.errors.any? %>
3 |
4 |
<%= pluralize(location.errors.count, "error") %> prohibited this location from being saved:
5 |
6 |
7 | <% location.errors.full_messages.each do |message| %>
8 | <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 |
16 | <%= form.label :name, class: 'text-sm text-caption-light font-medium uppercase' %>
17 |
18 |
19 | <%= form.text_field :name, class: 'input mb-2' %>
20 |
21 |
22 |
23 |
24 | <%= form.submit('Submit', class: 'bg-primary-dark hover:bg-primary text-white font-bold py-2 px-4 rounded inline-flex items-center mt-4') %>
25 |
26 |
27 | <% end %>
28 |
29 |
--------------------------------------------------------------------------------
/app/views/cohort_imports/new.html.erb:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | Change your password
2 |
3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= render "devise/shared/error_messages", resource: resource %>
5 | <%= f.hidden_field :reset_password_token %>
6 |
7 |
8 | <%= f.label :password, "New password" %>
9 | <% if @minimum_password_length %>
10 | (<%= @minimum_password_length %> characters minimum)
11 | <% end %>
12 | <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
13 |
14 |
15 |
16 | <%= f.label :password_confirmation, "Confirm new password" %>
17 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
18 |
19 |
20 |
21 | <%= f.submit "Change my password" %>
22 |
23 | <% end %>
24 |
25 | <%= render "users/shared/links" %>
26 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/models/cohort.rb:
--------------------------------------------------------------------------------
1 | class Cohort < ApplicationRecord
2 | has_paper_trail
3 |
4 | include OrganizationScope
5 | include CsvExportable
6 |
7 | belongs_to :male, class_name: 'Animal', optional: true
8 | belongs_to :female, class_name: 'Animal', optional: true
9 |
10 | has_many :animals
11 | has_many :measurements, as: :subject
12 | has_many :mortality_events
13 |
14 | belongs_to :enclosure, required: false
15 |
16 | validates :name, presence: true, uniqueness: { scope: :organization_id }
17 |
18 | delegate :name, to: :enclosure, prefix: true, allow_nil: true
19 |
20 | def self.exportable_columns
21 | %w[id name female_tag male_tag enclosure_name created_at updated_at]
22 | end
23 |
24 | delegate :tag, to: :female, prefix: true, allow_nil: true
25 | delegate :tag, to: :male, prefix: true, allow_nil: true
26 |
27 | def to_s
28 | name.blank? ? "Cohort #{id}" : name
29 | end
30 |
31 | def mortality_count
32 | mortality_events.count
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/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 https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/spec/models/enclosure_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Enclosure, type: :model do
4 | subject(:enclosure) { build_stubbed(:enclosure) }
5 |
6 | it "has associations" do
7 | is_expected.to belong_to(:location).optional
8 | is_expected.to belong_to(:organization)
9 | is_expected.to have_many(:measurements)
10 | end
11 |
12 | describe "Validations >" do
13 | subject(:enclosure) { build(:enclosure) }
14 |
15 | it "has a valid factory" do
16 | expect(enclosure).to be_valid
17 | end
18 |
19 | it_behaves_like OrganizationScope
20 |
21 | it { is_expected.to validate_uniqueness_of(:name).scoped_to(:organization_id, :location_id) }
22 | end
23 |
24 | describe "#empty?" do
25 | it "returns false with a cohort" do
26 | enclosure.cohort = build(:cohort)
27 | expect(enclosure).not_to be_empty
28 | end
29 |
30 | it "returns true without a cohort" do
31 | expect(enclosure).to be_empty
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/system/enclosures/new_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the enclosure New page", type: :system do
4 | let(:user) { create(:user) }
5 | let!(:location) { create(:location, organization_id: user.organization_id) }
6 |
7 | before do
8 | sign_in user
9 | end
10 |
11 | it "Then I see the enclosure form" do
12 | visit new_enclosure_path
13 |
14 | expect(page).to have_content("New Enclosure")
15 | expect(page).to have_content("Location")
16 | expect(page).to have_content("Name")
17 | expect(page).to have_select("enclosure_location_id", with_options: [location.name_with_facility])
18 | end
19 |
20 | it "I can create a new enclosure" do
21 | visit new_enclosure_path
22 |
23 | select(location.name, from: 'Location')
24 | fill_in('Name', with: "Frank's Enclosure")
25 | click_button('Submit')
26 |
27 | expect(page).to have_content("Frank's Enclosure")
28 | expect(page).to have_content(location.name)
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/assets/images/icons/pencil.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spec/system/delete_failed_file_upload_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "upload Measurement category", type: :system do
4 | include Devise::Test::IntegrationHelpers
5 |
6 | let(:user) { create(:user) }
7 |
8 | before do
9 | sign_in user
10 | end
11 |
12 | context 'when user file is not successfully uploaded' do
13 | it "user can removes the failed upload if it belong to the current organization" do
14 | processed_file = create(:processed_file, status: "Failed", organization: user.organization)
15 | processed_files_count = ProcessedFile.for_organization(user.organization).count
16 |
17 | visit file_uploads_path
18 |
19 | link = find("a[data-method='delete'][href='#{file_upload_path(processed_file.id)}']")
20 | within('tbody') do
21 | expect(page).to have_xpath('.//tr', count: processed_files_count)
22 | link.click
23 | expect(page).to have_xpath('.//tr', count: (processed_files_count - 1))
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/models/location_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Location, type: :model do
4 | subject(:location) { build_stubbed(:location) }
5 |
6 | it "has associations" do
7 | is_expected.to have_many(:enclosures)
8 | is_expected.to belong_to(:facility)
9 | end
10 |
11 | describe "Validations >" do
12 | subject(:location) { build_stubbed(:location) }
13 |
14 | it "has a valid factory" do
15 | expect(location).to be_valid
16 | end
17 |
18 | include_examples OrganizationScope
19 |
20 | it { is_expected.to validate_presence_of(:name) }
21 | it { is_expected.to validate_presence_of(:facility_id) }
22 | end
23 |
24 | describe "#name_with_facility" do
25 | before do
26 | location.name = "best location"
27 | location.facility.name = "best facility"
28 | end
29 |
30 | it "returns the name of the location combined with the name of the facility" do
31 | expect(location.name_with_facility).to eq("best facility - best location")
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/system/exit_types/index_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the exit type index page", type: :system do
4 | let(:user) { create(:user, role: "admin") }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see a list of exit_types" do
11 | exit_types = FactoryBot.create_list(:exit_type, 3, organization: user.organization)
12 |
13 | visit exit_types_path
14 |
15 | exit_types.each do |exit_type|
16 | expect(page).to have_content(exit_type.name)
17 | end
18 | end
19 |
20 | it "Then I can't see exit_types of another organization" do
21 | exit_type = FactoryBot.create(:exit_type)
22 |
23 | visit exit_types_path
24 |
25 | expect(page).not_to have_content(exit_type.name)
26 | end
27 |
28 | context "As a non-admin user" do
29 | it "Then I should not have access to the exit types index page" do
30 | user.update(role: "user")
31 |
32 | visit exit_types_path
33 |
34 | expect(page).to have_content 'Not authorized'
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/views/users/shared/_links.html.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end %>
4 |
5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end %>
8 |
9 |
10 |
11 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
12 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
13 | <% end %>
14 |
15 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
16 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
17 | <% end %>
18 |
19 | <%- if devise_mapping.omniauthable? %>
20 | <%- resource_class.omniauth_providers.each do |provider| %>
21 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
22 | <% end %>
23 | <% end %>
24 |
--------------------------------------------------------------------------------
/spec/support/shared_contexts/rake.rb:
--------------------------------------------------------------------------------
1 | require "rake"
2 |
3 | shared_context "rake" do
4 | let(:rake) { Rake::Application.new }
5 | # use the text we pass to `describe` to calculate the task we're going to run
6 | let(:task_name) { self.class.top_level_description }
7 | # `task_path` is the path to the file itself, relative to `Rails.root`
8 | let(:task_path) { "lib/tasks/#{task_name.split(':').first}" }
9 | subject { rake[task_name] }
10 |
11 | # exclude the path to the task we're testing so we have the task available.
12 | # this only matters when you're running more than one test on a rake task
13 | def loaded_files_excluding_current_rake_file
14 | $LOADED_FEATURES.reject { |file| file == Rails.root.join("#{task_path}.rake").to_s }
15 | end
16 |
17 | before do
18 | Rake.application = rake
19 | Rake.application.rake_require(task_path, [Rails.root.to_s], loaded_files_excluding_current_rake_file)
20 |
21 | # load Rails stack from inside the lib folder
22 | Rake::Task.define_task(:environment)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
24 | <%= image_tag 'fogg.png' %>
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/views/file_uploads/upload.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
File Upload(s)
4 |
5 | <%= link_to(new_file_upload_path) do %>
6 |
7 | Upload More Files
8 |
9 | <% end %>
10 | <%= link_to(file_uploads_path) do %>
11 |
12 | View All Files
13 |
14 | <% end %>
15 |
16 | <% @file_uploads.each do |file_upload| %>
17 |
18 | <%= file_upload.result_message %>
19 | Category: <%= file_upload.category %>
20 | Filename: <%= file_upload.filename %>
21 |
22 | <% end %>
23 |
24 |
25 |
--------------------------------------------------------------------------------
/spec/models/exit_type_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe ExitType, type: :model do
4 | subject(:exit_type) { build_stubbed(:exit_type) }
5 |
6 | describe "Validations >" do
7 | subject(:exit_type) { build(:exit_type) }
8 |
9 | it "has a valid factory" do
10 | expect(exit_type).to be_valid
11 | end
12 |
13 | it_behaves_like OrganizationScope
14 |
15 | it { is_expected.to validate_presence_of(:name) }
16 |
17 | context "with an associated mortality_events" do
18 | subject(:exit_type) { create(:exit_type) }
19 |
20 | before do
21 | create(:mortality_event, exit_type: exit_type)
22 | end
23 |
24 | it "cannot be destroyed" do
25 | expect { exit_type.destroy }
26 | .not_to change(ExitType, :count)
27 | end
28 |
29 | it "adds an error to the exit_type" do
30 | exit_type.destroy
31 | expect(exit_type.errors.full_messages)
32 | .to eq(["Cannot delete record because dependent mortality events exist"])
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/tasks/scheduler.rake:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | namespace :scheduler do
4 | desc 'Delete stale temporary files'
5 | # This rake task is meant to be run once per day to cleanse temporary files
6 | # from the database. It will also log if any temporary files have not been
7 | # successfully processed.
8 | task delete_stale_temporary_files: :environment do
9 | # collect all temporary_files over a week old
10 | TemporaryFile.where('created_at < ?', 7.days.ago).each do |file|
11 | # check if they have been processed successfully
12 | if file.processed_file && file.processed_file.status == 'Processed'
13 | # if yes, then we are safe to destroy the file
14 | file.destroy
15 | else
16 | # if no, then we log the file to be handled later
17 | warning_message = <<-MESSAGE
18 | Warning: TemporaryFile with id #{file.id} is more than
19 | 7 days old but has not been processed successfully.
20 | MESSAGE
21 | Rails.logger.warn warning_message
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/system/users/update_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the user Edit page", type: :system do
4 | let(:organization) { create(:organization) }
5 | let(:admin) { create(:user, :admin, organization: organization) }
6 |
7 | before do
8 | sign_in admin
9 | end
10 |
11 | context "As an admin user" do
12 | it "Then I can update a user" do
13 | user = create(:user, organization: organization)
14 |
15 | visit edit_user_path(user)
16 |
17 | within('form') do
18 | fill_in('Email', with: "another@email.com")
19 | fill_in('Password', with: "anewpassword")
20 | click_on('Submit')
21 | end
22 |
23 | expect(page).to have_content('User was successfully updated.')
24 | expect(page).to have_content('another@email.com')
25 | end
26 | end
27 |
28 | it "I can't update a measurement type of another organization" do
29 | user = create(:user)
30 |
31 | visit edit_user_path(user)
32 |
33 | expect(page).to have_content 'You are not authorized to access this resource.'
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console:0 */
2 | // This file is automatically compiled by Webpack, along with any other files
3 | // present in this directory. You're encouraged to place your actual application logic in
4 | // a relevant structure within app/javascript and only use these pack files to reference
5 | // that code so it'll be compiled.
6 | //
7 | // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
8 | // layout file, like app/views/layouts/application.html.erb
9 |
10 |
11 | // Uncomment to copy all static images under ../images to the output folder and reference
12 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
13 | // or the `imagePath` JavaScript helper below.
14 | //
15 | // const images = require.context('../images', true)
16 | // const imagePath = (name) => images(name, true)
17 | //= require jquery
18 | //= require jquery_ujs
19 |
20 | $(function() {
21 | setTimeout(function(){
22 | $("[role='alert']").slideUp(500);
23 | }, 10000);
24 | });
25 |
26 | import "controllers"
27 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2019 Ruby for Good
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/spec/requests/measurements_index_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'When I visit the Measurements page', type: :system do
4 | let(:organization2) { create(:organization, name: 'Black Abalone') }
5 | let(:user) { create(:user, organization: organization) }
6 | let!(:measurement2) { create(:measurement, organization: organization2, value: organization2.id) }
7 | let(:organization) { create(:organization, name: 'White Abalone') }
8 | let!(:measurements) { create_list(:measurement, 3, organization: user.organization, value: organization.id) }
9 |
10 | before do
11 | sign_in user
12 | visit measurements_path
13 | end
14 |
15 | it 'Displays measurements for current organizations', :aggregate_failures do
16 | within('tbody') do
17 | expect(page).to have_xpath('.//tr', count: measurements.count)
18 | expect(page).to have_content organization.id
19 | end
20 | end
21 |
22 | it 'Then will not display second organization measurement' do
23 | within('tbody') do
24 | expect(page).not_to have_content organization2.id
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/system/animals/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the animal Show page", type: :system do
4 | it "I see information of a specific animal" do
5 | user = create(:user, :admin)
6 | cohort = create(:cohort)
7 | animal = create(:animal, cohort: cohort, organization: user.organization)
8 |
9 | sign_in user
10 |
11 | visit animal_path(animal)
12 |
13 | expect(page).to have_content(animal.entry_year)
14 | expect(page).to have_content(animal.entry_date)
15 | expect(page).to have_content(animal.entry_point)
16 | expect(page).to have_content(animal.tag)
17 | expect(page).to have_content(animal.sex.titleize)
18 | expect(page).to have_content(animal.cohort.name)
19 | end
20 |
21 | it "I can't see an animal of another organization" do
22 | user = create(:user, :admin)
23 | cohort = create(:cohort)
24 | animal = create(:animal, cohort: cohort)
25 |
26 | sign_in user
27 |
28 | visit animal_path(animal)
29 |
30 | expect(page).to have_content("You are not authorized to access this resource.")
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/capistrano/tasks/envvars.rake:
--------------------------------------------------------------------------------
1 | namespace :envvars do
2 | desc 'Load environment variables'
3 | task :load do
4 | # Grab the current value of :default_env
5 | environment = fetch(:default_env, {})
6 |
7 | on roles(:app) do
8 | # Read in the environment file
9 | lines = capture("cat #{fetch(:deploy_to)}/.environment")
10 | lines.each_line do |line|
11 | # Remove the "export " keyword, we have no use for that here
12 | line = line.sub /^export /, ""
13 | # Clean up the input by removing line breaks, tabs etc
14 | line = line.gsub /[\t\r\n\f]+/, ""
15 |
16 | # Grab the key and value from the line
17 | key, value = line.split("=")
18 |
19 | # Remove surrounding quotes if present
20 | value = value.slice(1..-2) if value.start_with?('"') && value.end_with?('"')
21 |
22 | # Store the value in our :default_env copy
23 | environment.store(key, value)
24 | end
25 |
26 | # Finally, update the global :default_env variable again
27 | set :default_env, environment
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/views/enclosures/csv_upload.html.erb:
--------------------------------------------------------------------------------
1 |
28 |
--------------------------------------------------------------------------------
/spec/system/cohorts/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the cohort Show page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see information of a specific cohort" do
11 | cohort = create(:cohort, organization: user.organization)
12 | enclosure = create(:enclosure)
13 |
14 | cohort.enclosure = enclosure
15 | cohort.save
16 |
17 | visit cohort_path(cohort)
18 |
19 | within('.container') do
20 | expect(page).to have_content cohort.name
21 | expect(page).to have_content cohort.female_tag
22 | expect(page).to have_content cohort.male_tag
23 | expect(page).to have_content cohort.enclosure.name
24 | end
25 | end
26 |
27 | it "I can't see a cohort of another organization" do
28 | cohort = create(:cohort)
29 | enclosure = create(:enclosure)
30 |
31 | cohort.enclosure = enclosure
32 | cohort.save
33 |
34 | visit cohort_path(cohort)
35 |
36 | expect(page).to have_content("You are not authorized to access this resource.")
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/system/facilities/update_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the facility Edit page", type: :system do
4 | let(:user) { create(:user) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "And fill out the form and click the submit button, the facility is updated" do
11 | facility = create(:facility, organization: user.organization)
12 |
13 | visit edit_facility_path(facility)
14 |
15 | within('form') do
16 | fill_in 'facility_name', with: 'Aquarium of the Pacific'
17 | fill_in 'facility_code', with: 'AOP'
18 | click_on 'Submit'
19 | end
20 |
21 | expect(page).to have_content 'Facility was successfully updated.'
22 | within('.container') do
23 | expect(page).to have_content 'Aquarium of the Pacific'
24 | expect(page).to have_content 'AOP'
25 | end
26 | end
27 |
28 | it "I can't update a facility of another organization" do
29 | facility = create(:facility)
30 |
31 | visit edit_facility_path(facility)
32 |
33 | expect(page).to have_content 'You are not authorized to access this resource.'
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/system/measurement_types/new_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the measurement_type New page", type: :system do
4 | let(:user) { create(:user, role: "admin") }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I see the measurement_type form" do
11 | visit new_measurement_type_path
12 |
13 | expect(page).to have_content("New Measurement Type")
14 | expect(page).to have_content("Name")
15 | expect(page).to have_content("Unit")
16 | end
17 |
18 | it "I can create a new measurement_type" do
19 | visit new_measurement_type_path
20 |
21 | fill_in('Name', with: "Length")
22 | fill_in('Unit', with: "cm")
23 | click_button('Submit')
24 |
25 | expect(page).to have_content("Length")
26 | expect(page).to have_content("cm")
27 | end
28 |
29 | context "As a non-admin user" do
30 | it "Then I should not have access to the measurement types action form" do
31 | user.update(role: "user")
32 |
33 | visit new_measurement_type_path
34 |
35 | expect(page).to have_content 'Not authorized'
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/db/migrate/20200922203301_create_active_storage_tables.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20170806125915)
2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
3 | def change
4 | create_table :active_storage_blobs do |t|
5 | t.string :key, null: false
6 | t.string :filename, null: false
7 | t.string :content_type
8 | t.text :metadata
9 | t.bigint :byte_size, null: false
10 | t.string :checksum, null: false
11 | t.datetime :created_at, null: false
12 |
13 | t.index [ :key ], unique: true
14 | end
15 |
16 | create_table :active_storage_attachments do |t|
17 | t.string :name, null: false
18 | t.references :record, null: false, polymorphic: true, index: false
19 | t.references :blob, null: false
20 |
21 | t.datetime :created_at, null: false
22 |
23 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
24 | t.foreign_key :active_storage_blobs, column: :blob_id
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/system/users/delete_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the users Index page", type: :system do
4 | let(:organization) { create(:organization) }
5 | let(:user) { create(:user, :admin, organization: organization) }
6 |
7 | before do
8 | sign_in user
9 | end
10 |
11 | it "Then I click the delete button, user should be deleted" do
12 | user1 = create(:user, organization: user.organization)
13 |
14 | visit users_path
15 |
16 | link = find("a[data-method='delete'][href='#{user_path(user1.id)}']")
17 | within('tbody') do
18 | expect do
19 | link.click
20 | end.to change { page.all(:xpath, './/tr').count }.by(-1)
21 | end
22 | end
23 |
24 | it "Then I cannot delete myself" do
25 | create(:user, organization: user.organization)
26 | users_count = User.for_organization(user.organization).count
27 |
28 | visit users_path
29 |
30 | within('tbody') do
31 | expect(page).to have_xpath('.//tr', count: users_count)
32 | expect(page).not_to have_selector("a[data-method='delete'][href='#{user_path(user.id)}']")
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_variables.scss:
--------------------------------------------------------------------------------
1 | $color-primary: #9d9dd3;
2 | $color-secondary: #eb5757;
3 | $color-dark: #333333;
4 | $color-light: #ffffff;
5 | $color-primary-light: rgba($color: $color-primary, $alpha: 0.7);
6 | $color-primary-dark: #6767ca;
7 | $color-warning: #f2994a;
8 | $color-success: #27ae60;
9 | $color-dark-light: #eee;
10 | $color-green-bg: #67CAB8;
11 | $color-grey-bg: #EEEEEE;
12 |
13 | $body-height: calc(100vh - 38px);
14 |
15 | .color-primary {
16 | color: $color-primary !important;
17 | }
18 |
19 | .bg-primary {
20 | background-color: $color-primary !important;
21 | }
22 |
23 | .color-secondary {
24 | color: $color-secondary !important;
25 | }
26 |
27 | .bg-secondary {
28 | background-color: $color-secondary !important;
29 | }
30 |
31 | .bg-secondary {
32 | background-color: $color-secondary !important;
33 | }
34 |
35 | .bg-dark-light {
36 | background-color: $color-dark-light !important;
37 | }
38 |
39 | .bg-dark {
40 | background-color: $color-dark !important;
41 | }
42 |
43 | .color-light {
44 | color: $color-light !important;
45 | }
46 |
47 | .bg-light {
48 | background-color: $color-light !important;
49 | }
--------------------------------------------------------------------------------
/app/models/facility.rb:
--------------------------------------------------------------------------------
1 | # rubocop:disable Lint/RedundantCopDisableDirective, Layout/LineLength
2 | # == Schema Information
3 | #
4 | # Table name: facilities
5 | #
6 | # id :bigint not null, primary key
7 | # name :string
8 | # code :string
9 | # created_at :datetime not null
10 | # updated_at :datetime not null
11 | #
12 | # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective
13 |
14 | class Facility < ApplicationRecord
15 | has_paper_trail
16 |
17 | include OrganizationScope
18 | include CsvExportable
19 |
20 | has_many :locations
21 |
22 | validates :name, presence: true
23 | validates :code, presence: true, uniqueness: { scope: :organization_id }
24 |
25 | after_commit { Rails.cache.delete('facility_codes') }
26 |
27 | def self.valid_codes
28 | Rails.cache.fetch('facility_codes') { pluck(:code).map(&:upcase) }
29 | end
30 |
31 | def self.valid_code?(code)
32 | valid_codes.include?(code.upcase)
33 | end
34 |
35 | # Replaced by blazer reporting - 1/24/21
36 | # ReportsKit uses this for default labeling
37 | def to_s
38 | code
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.0.3-alpine AS builder
2 |
3 | ARG RAILS_ROOT=/usr/src/app/
4 | WORKDIR $RAILS_ROOT
5 |
6 | RUN apk add --update --no-cache \
7 | build-base \
8 | curl-dev \
9 | nodejs \
10 | postgresql-dev \
11 | tzdata \
12 | git \
13 | yarn
14 |
15 | COPY package*.json yarn.lock Gemfile* $RAILS_ROOT
16 | RUN yarn install --check-files --frozen-lockfile &&\
17 | bundle config --global frozen 1 && bundle install
18 |
19 | ### BUILD STEP DONE ###
20 |
21 | FROM ruby:3.0.3-alpine
22 |
23 | ARG RAILS_ROOT=/usr/src/app/
24 | WORKDIR $RAILS_ROOT
25 |
26 | RUN set -eux; \
27 | addgroup -S app; \
28 | adduser -S -D -G app -H -h $RAILS_ROOT -s /bin/sh app; \
29 | chown -R app:app $RAILS_ROOT
30 |
31 | RUN apk add --update --no-cache \
32 | bash\
33 | nodejs \
34 | postgresql-client \
35 | su-exec \
36 | tzdata \
37 | yarn \
38 | && rm -rf /var/cache/apk/*
39 |
40 | COPY --from=builder $RAILS_ROOT $RAILS_ROOT
41 | COPY --from=builder /usr/local/bundle/ /usr/local/bundle/
42 |
43 | COPY . .
44 |
45 | RUN chown -R app:app $RAILS_ROOT
46 |
47 | EXPOSE 3000
48 |
49 | ENTRYPOINT ["./docker-entrypoint.sh"]
50 | CMD ["abalone"]
51 |
--------------------------------------------------------------------------------
/app/models/processed_file.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # rubocop:disable Lint/RedundantCopDisableDirective, Layout/LineLength
4 | # == Schema Information
5 | #
6 | # Table name: processed_files
7 | #
8 | # id :bigint not null, primary key
9 | # filename :string
10 | # original_filename :string
11 | # category :string
12 | # status :string
13 | # job_stats :jsonb not null
14 | # job_errors :text
15 | # created_at :datetime not null
16 | # updated_at :datetime not null
17 | #
18 | # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective
19 |
20 | class ProcessedFile < ApplicationRecord
21 | include OrganizationScope
22 |
23 | belongs_to :temporary_file,
24 | optional: true,
25 | inverse_of: :processed_file,
26 | dependent: :destroy
27 | belongs_to :organization
28 |
29 | validates :job_stats, exclusion: { in: [nil, ""] }
30 |
31 | after_initialize :set_default_job_stats, if: :new_record?
32 |
33 | private
34 |
35 | def set_default_job_stats
36 | self.job_stats = {}
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/views/facilities/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: facility, local: true, html: { class: "w-full max-w-md" }) do |form| %>
2 | <% if facility.errors.any? %>
3 |
4 |
<%= pluralize(facility.errors.count, "error") %> prohibited this facility from being saved:
5 |
6 | <% facility.errors.full_messages.each do |message| %>
7 | <%= message %>
8 | <% end %>
9 |
10 |
11 | <% end %>
12 |
13 |
14 | <%= form.label :name, class: 'text-sm text-caption-light font-medium uppercase' %>
15 |
16 |
17 | <%= form.text_field :name, class: 'input mb-2' %>
18 |
19 |
20 |
21 |
22 | <%= form.label :code, class: 'text-sm text-caption-light font-medium uppercase' %>
23 |
24 |
25 | <%= form.text_field :code, class: 'input mb-2' %>
26 |
27 |
28 |
29 |
30 | <%= form.submit('Submit', class: 'bg-primary-dark hover:bg-primary text-white font-bold py-2 px-4 rounded inline-flex items-center mt-4') %>
31 |
32 |
33 | <% end %>
34 |
--------------------------------------------------------------------------------
/spec/system/enclosures/update_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the enclosure Edit page", type: :system do
4 | let(:user) { create(:user) }
5 | let!(:location) { create(:location, organization_id: user.organization_id) }
6 |
7 | before do
8 | sign_in user
9 | end
10 |
11 | it "And fill out the form and click the submit button, enclosure should be updated" do
12 | enclosure = create(:enclosure, organization: user.organization)
13 |
14 | visit edit_enclosure_path(enclosure)
15 |
16 | within('form') do
17 | select(location.name_with_facility, from: 'Location')
18 | fill_in 'enclosure_name', with: "Gary's old enclosure"
19 | click_on 'Submit'
20 | end
21 |
22 | expect(page).to have_content 'Enclosure was successfully updated.'
23 | expect(page).to have_content(location.name)
24 | expect(page).to have_content "Gary's old enclosure"
25 | end
26 |
27 | it "I can't update an enclosure of another organization" do
28 | enclosure = create(:enclosure)
29 |
30 | visit edit_enclosure_path(enclosure)
31 |
32 | expect(page).to have_content 'You are not authorized to access this resource.'
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/system/measurement_types/destroy_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the measurement_type Index page", type: :system do
4 | let(:user) { create(:user, role: "admin") }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | it "Then I click the delete button, measurement_type should be deleted" do
11 | measurement_type1 = create(:measurement_type, organization: user.organization)
12 | measurement_type_count = MeasurementType.for_organization(user.organization).count
13 |
14 | visit measurement_types_path
15 |
16 | link = find("a[data-method='delete'][href='#{measurement_type_path(measurement_type1.id)}']")
17 |
18 | within('tbody') do
19 | expect(page).to have_xpath('.//tr', count: measurement_type_count)
20 | link.click
21 | expect(page).to have_xpath('.//tr', count: (measurement_type_count - 1))
22 | end
23 | end
24 |
25 | context "As a non-admin user" do
26 | it "Then I should not have access to the measurement types action form" do
27 | user.update(role: "user")
28 |
29 | visit measurement_types_path
30 |
31 | expect(page).to have_content 'Not authorized'
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/app/views/measurement_types/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: measurement_type, local: true, html: { class: "w-full max-w-md" }) do |form| %>
2 | <% if measurement_type.errors.any? %>
3 |
4 |
<%= pluralize(measurement_type.errors.count, "error") %> prohibited this measurement_type from being saved:
5 |
6 |
7 | <% measurement_type.errors.full_messages.each do |message| %>
8 | <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 |
16 | <%= form.label :name, class: 'text-sm text-caption-light font-medium uppercase' %>
17 |
18 |
19 | <%= form.text_field :name, class: 'input mb-2' %>
20 |
21 |
22 |
23 | <%= form.label :unit, class: 'text-sm text-caption-light font-medium uppercase' %>
24 |
25 |
26 | <%= form.text_field :unit, class: 'input mb-2' %>
27 |
28 |
29 |
30 | <%= form.submit('Submit', class: 'bg-primary-dark hover:bg-primary text-white font-bold py-2 px-4 rounded inline-flex items-center mt-4') %>
31 |
32 |
33 | <% end %>
34 |
--------------------------------------------------------------------------------
/app/views/file_uploads/show_processing_csv_errors.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Detailed processed file with errors
4 |
5 |
6 | <%= link_to "
Upload New File ".html_safe, new_file_upload_path %>
7 |
8 |
9 | Filename: <%= @processed_file.filename %> processed at <%= @processed_file.updated_at %>
10 | Processed At: <%= @processed_file.updated_at %>
11 | Category: <%= @processed_file.category %>
12 | Statistics: <%= @processed_file.job_stats %>
13 |
14 |
15 |
16 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/views/reports/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Reports
6 |
7 |
8 |
9 |
10 | <%= link_to "Total Animals by Cohort", reports_path %>
11 |
12 |
13 |
14 |
15 |
16 |
17 | <%= render_report 'total_animals_by_cohort',
18 | context_params: { organization_id: current_user.organization_id },
19 | actions: [] do |report| %>
20 | <%= report.form do |f| %>
21 |
22 | <%= f.multi_autocomplete :cohort, placeholder: 'Cohort...' %>
23 | <%= f.date_range :created_at %>
24 |
25 | <% end %>
26 |
27 | <%= report.export_csv_button 'Download CSV', class: 'button-primary' %>
28 | <%= report.export_xls_button 'Download XLS', class: 'button-primary' %>
29 |
30 | <% end %>
31 |
32 |
33 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path('..', __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to setup or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies
21 | system! 'bin/yarn'
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:prepare'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/app/views/file_uploads/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Processed File
4 |
5 |
6 | File name:
7 |
8 |
9 | <%= @processed_file.filename %>
10 |
11 |
12 |
13 |
14 | Processed at:
15 |
16 |
17 | <%= @processed_file.updated_at %>
18 |
19 |
20 |
21 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/views/measurement_types/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
<%= notice %>
4 |
5 | <%= link_to measurement_types_path do %>
6 |
7 | << Back
8 |
9 | <% end %>
10 | <%= link_to edit_measurement_type_path(@measurement_type) do %>
11 |
12 | Edit Measurement Type
13 |
14 | <% end %>
15 |
16 |
17 |
Measurement Type
18 |
19 |
20 |
Name
21 | <%= @measurement_type.name %>
22 |
Unit
23 | <%= @measurement_type.unit %>
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/views/devise/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
32 |
33 | <%= image_tag 'fogg.png' %>
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | include Pagy::Backend
3 | before_action :authenticate_user!
4 | before_action :set_paper_trail_whodunnit
5 | before_action :set_current_user
6 |
7 | helper_method def current_organization
8 | current_user.organization
9 | end
10 |
11 | def authorize_admin!
12 | redirect_to root_path, alert: "Not authorized" unless current_user.admin?
13 | end
14 |
15 | # Auth check for Blazer reporting
16 | def require_organization
17 | redirect_to root_path unless current_user && current_organization
18 | end
19 |
20 | # Save current_user to a place Blazer and models can access it.
21 | def set_current_user
22 | Current.user = current_user
23 | end
24 |
25 | # Blazer's before_action_method call to
26 | # make sure a user is logged in and associated
27 | # with an organizatio as the app does.
28 | def blazer_setup
29 | require_organization
30 | set_current_user
31 | end
32 |
33 | # Error page for CanCanCan
34 | rescue_from CanCan::AccessDenied do
35 | flash[:alert] = 'You are not authorized to access this resource.'
36 | redirect_back fallback_location: root_path, status: 302
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/system/users/new_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the User New page", type: :system do
4 | let(:user) { create(:user, :admin) }
5 |
6 | before do
7 | sign_in user
8 | end
9 |
10 | context "As an admin user" do
11 | it "Then I see a user form" do
12 | visit new_user_path
13 |
14 | expect(page).to have_content("New User")
15 | expect(page).to have_content("Email")
16 | expect(page).to have_content("Password")
17 | expect(page).to have_select("user_role", with_options: %w[user admin])
18 | end
19 |
20 | it "I can create a new user" do
21 | visit new_user_path
22 |
23 | fill_in("Email", with: "test@example.com")
24 | fill_in("Password", with: "password")
25 | select("user", from: 'Role')
26 | click_button('Submit')
27 |
28 | expect(page).to have_content("test@example.com")
29 | expect(page).to have_content(user.organization.name)
30 | end
31 | end
32 |
33 | context "As a non-admin user" do
34 | it "Then I should not have access to the user form" do
35 | user.update(role: "user")
36 |
37 | visit new_user_path
38 |
39 | expect(page).to have_content 'Not authorized'
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/app/views/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 |
33 |
--------------------------------------------------------------------------------
/db/migrate/20200927001420_install_blazer.rb:
--------------------------------------------------------------------------------
1 | class InstallBlazer < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :blazer_queries do |t|
4 | t.references :creator
5 | t.string :name
6 | t.text :description
7 | t.text :statement
8 | t.string :data_source
9 | t.timestamps null: false
10 | end
11 |
12 | create_table :blazer_audits do |t|
13 | t.references :user
14 | t.references :query
15 | t.text :statement
16 | t.string :data_source
17 | t.datetime :created_at
18 | end
19 |
20 | create_table :blazer_dashboards do |t|
21 | t.references :creator
22 | t.string :name
23 | t.timestamps null: false
24 | end
25 |
26 | create_table :blazer_dashboard_queries do |t|
27 | t.references :dashboard
28 | t.references :query
29 | t.integer :position
30 | t.timestamps null: false
31 | end
32 |
33 | create_table :blazer_checks do |t|
34 | t.references :creator
35 | t.references :query
36 | t.string :state
37 | t.string :schedule
38 | t.text :emails
39 | t.text :slack_channels
40 | t.string :check_type
41 | t.text :message
42 | t.datetime :last_run_at
43 | t.timestamps null: false
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | before_action :authorize_admin!
3 | load_and_authorize_resource
4 |
5 | def index
6 | @users = current_organization.users
7 | end
8 |
9 | def show
10 | redirect_to users_path unless @user
11 | end
12 |
13 | def new
14 | @user = User.new
15 | end
16 |
17 | def create
18 | @user = User.new(user_params)
19 | if @user.save
20 | redirect_to user_path(@user), notice: 'User was successfully created.'
21 | else
22 | render :new
23 | end
24 | end
25 |
26 | def edit; end
27 |
28 | def update
29 | if @user.update(user_params)
30 | redirect_to @user, notice: 'User was successfully updated.'
31 | else
32 | render :edit
33 | end
34 | end
35 |
36 | def destroy
37 | notice_message = 'User cannot delete itself.'
38 |
39 | unless @user == current_user
40 | @user.destroy
41 | notice_message = 'User was successfully destroyed.'
42 | end
43 |
44 | redirect_to users_url, notice: notice_message
45 | end
46 |
47 | private
48 |
49 | def user_params
50 | params.require(:user).permit(
51 | :email,
52 | :password,
53 | :role
54 | ).merge(organization_id: current_organization.id)
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/spec/system/users/show_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "When I visit the user Show page", type: :system do
4 | it "as an admin, I see information of a specific user" do
5 | user = create(:user, :admin)
6 | sign_in user
7 |
8 | visit user_path(user)
9 |
10 | expect(page).to have_content(user.email)
11 | expect(page).to have_content(user.organization.name)
12 | end
13 |
14 | it "as a user, I see information of a specific user" do
15 | user = create(:user)
16 | sign_in user
17 |
18 | visit user_path(user)
19 |
20 | expect(page).not_to have_content(user.email)
21 | expect(page).to have_content(user.organization.name)
22 | end
23 |
24 | it "as a user who's not logged in, I see no user specific information" do
25 | user = create(:user)
26 |
27 | visit user_path(user)
28 |
29 | expect(page).not_to have_content(user.email)
30 | expect(page).not_to have_content("Your organization: #{user.organization.name}")
31 | end
32 |
33 | it "I can't see a facility of another organization" do
34 | user = create(:user, :admin)
35 | sign_in user
36 |
37 | another_user = create(:user)
38 | visit user_path(another_user)
39 |
40 | expect(page).to have_content("You are not authorized to access this resource.")
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/app/views/enclosures/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with(model: enclosure, local: true, html: { class: "w-full" }) do |form| %>
2 | <% if enclosure.errors.any? %>
3 |
4 |
<%= pluralize(enclosure.errors.count, "error") %> prohibited this enclosure from being saved:
5 |
6 |
7 | <% enclosure.errors.full_messages.each do |message| %>
8 | <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 |
16 | <%= form.label :location_id, class: 'text-sm text-caption-light font-medium uppercase' %>
17 |
18 |
19 | <%= form.collection_select :location_id, @locations, :id, :name_with_facility, { include_blank: 'Select Location' }, { class: 'input w-full mb-2' } %>
20 |
21 |
22 |
23 |
24 |
25 | <%= form.label :name, class: 'text-sm text-caption-light font-medium uppercase' %>
26 |
27 |
28 | <%= form.text_field :name, class: 'input w-full mb-2' %>
29 |
30 |
31 |
32 |
33 |
34 | <%= form.submit('Submit', class: 'bg-primary-dark hover:bg-primary text-white font-bold py-2 px-4 rounded inline-flex items-center mt-4') %>
35 |
36 |
37 | <% end %>
38 |
--------------------------------------------------------------------------------
/spec/models/processed_file_spec.rb:
--------------------------------------------------------------------------------
1 | # rubocop:disable Lint/RedundantCopDisableDirective, Layout/LineLength
2 | # == Schema Information
3 | #
4 | # Table name: processed_files
5 | #
6 | # id :bigint not null, primary key
7 | # filename :string
8 | # original_filename :string
9 | # category :string
10 | # status :string
11 | # job_stats :jsonb not null
12 | # job_errors :text
13 | # created_at :datetime not null
14 | # updated_at :datetime not null
15 | #
16 | # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective
17 |
18 | require 'rails_helper'
19 |
20 | RSpec.describe ProcessedFile, type: :model do
21 | subject(:processed_file) { build_stubbed(:processed_file) }
22 |
23 | it "has associations" do
24 | is_expected.to belong_to(:temporary_file).dependent(:destroy).optional
25 | is_expected.to belong_to(:organization)
26 | end
27 |
28 | describe "Validations >" do
29 | subject(:processed_file) { build(:processed_file) }
30 |
31 | it "has a valid factory" do
32 | expect(processed_file).to be_valid
33 | end
34 |
35 | it_behaves_like OrganizationScope
36 |
37 | it { is_expected.to validate_exclusion_of(:job_stats).in_array([nil, ""]) }
38 | end
39 | end
40 |
--------------------------------------------------------------------------------