<%= render(Modal::GroupComponent.new(id: id)) do |component| %>
<% component.with_header(text: "Delete Contact Topic?", id: id) %>
<% component.with_body(text: [
"This topic and its related questions will be deleted and will no longer be presented while filling out case contacts.",
"This will not affect case contacts that have already been created."]) %>
<% component.with_footer do %>
<%= link_to soft_delete_contact_topic_path(contact_topic), method: :delete,
class: "btn-sm main-btn danger-btn btn-hover ms-auto" do %>
Delete Court Report Topic
<% end %>
<% end %>
<% end %>
<% end %>
<%= form.label :value, "Discussion Notes", class: "form-label" %>
<%= form.text_area(
:value,
data: { action: "input->autosave#save" },
rows: 3,
class: ["form-control", "contact-topic-answer-input"],
placeholder: "Enter discussion notes here to be included in the court report. Refer to individuals by role, not by name."
) %>
<% if current_user.casa_admin? %>
Visit <%= link_to "Manage Case Contact Topics", edit_casa_org_path(current_organization, anchor: "case-contact-topics") %> to set your organization Court report topics.
<% else %>
Your organization has not set any Court Report Topics yet. Contact your admin to learn more.
<% end %>
<% else %>
<% select_options = @contact_topics.map { |topic| [topic.question, topic.id] } %>
<%= form.fields_for :contact_topic_answers, ContactTopicAnswer.new, child_index: "NEW_RECORD" do |topic_fields| %>
<%= render("contact_topic_answer", form: topic_fields, select_options:) %>
<% end %>
<% if form.object.contact_topic_answers.any? %>
<%= form.fields_for :contact_topic_answers do |topic_fields| %>
<%= render("contact_topic_answer", form: topic_fields, select_options:) %>
<% end %>
<% end %>
<% end %>
<% if form.object.notes.present? || @contact_topics.empty? %>
<% end %>
No changes have been saved.
<% org_driving_reimbursement = current_organization.show_driving_reimbursement %>
<% show_driving_reimbursement = org_driving_reimbursement && show_volunteer_reimbursement(@casa_cases) %>
<% org_additional_expenses = current_organization.additional_expenses_enabled %>
<% show_additional_expenses = org_additional_expenses && Pundit.policy(current_user, @case_contact).additional_expenses_allowed? %>
<% if show_driving_reimbursement %>
Reimbursement
<% if Flipper.enabled?(:reimbursement_warning, current_organization) %>
Volunteers are reimbursed at the federal mileage rate. Please note that there is a $35.00 per month cap per volunteer for your mileage.We aim to mail your reimbursement to you via check within 14-28 business days of your request for reimbursement.
<% end %>
<%= params[:filterrific] ?
"No case contacts have been found." :
"You have no case contacts for this case. \
Please click New Case Contact button above to create a case contact for your youth!" %>
<% else %>
<%= params[:filterrific] ?
"No case contacts have been found." :
"You have no case contacts for this case. \
Please click New Case Contact button above to create a case contact for your youth!" %>
<% end %>
<% end %>
<%== pagy_bootstrap_nav(@pagy) %>
<% end %>
================================================
FILE: app/views/case_court_reports/_generate_docx.html.erb
================================================
<%= form_with url: generate_case_court_reports_path, local: false do |form| %>
<% id = "generate-docx-report-modal" %>
<%= render(Modal::OpenButtonComponent.new(target: id, klass: "btn generate-report-button")) do %>
Download Court Report as .docx
The Court Report is pre-filled with information for your case. You can select among currently active cases assigned to you. The document is in Microsoft Word format (.docx).
<% end %>
<%= render(Modal::GroupComponent.new(id: id)) do |component| %>
<% component.with_header(text: "Download Court Report as a .docx", id: id, klass: "content-1") %>
<% component.with_body do %>
To download a court report, choose an active case and specify the date range.
<%= form.label :case_selection, "Case" %>
<% select_options = @assigned_cases.map { |casa_case| casa_case.decorate.court_report_select_option } %>
<% show_search = !current_user.volunteer? %>
<% select_case_prompt = show_search ? "Search by volunteer name or case number" : "Select case number" %>
<% select2_class = show_search ? " select2" : "" %>
Case groups are used to bulk create court dates for all cases in a group. For example, if siblings attend the same court data you can create a case group for them and then use the
<%= link_to 'Bulk Court Date', new_bulk_court_date_path %>
form to create a court date for all of them.
<%= link_to new_case_group_path, class: "btn-sm main-btn primary-btn btn-hover" do %>
New Case Group
<% end %>
<%= time_ago_in_words(case_group.updated_at) %> ago
<%= link_to edit_case_group_path(case_group) do %>
<% end %>
<%= link_to 'Delete', case_group_path(case_group), class: 'btn btn-danger', method: :delete, data: { confirm: "Are you sure that you want to delete this case group?" } %>
<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
If you weren't expecting this email, please disregard.
================================================
FILE: app/views/devise/mailer/email_changed.html.erb
================================================
<% if @resource.try(:unconfirmed_email?) %>
Your CASA account's email has been updated to <%= @resource.unconfirmed_email %>.
<% else %>
Your CASA account's email has been updated to <%= @resource.email %>.
<% end %>
================================================
FILE: app/views/devise/mailer/invitation_instructions.html.erb
================================================
<% if @resource.is_a?(User) %>
A <%= @resource.casa_org.display_name %>’s County <%= @resource.type %> console account has been created for you.
This console is for logging the time you spend and actions you take on your CASA case.
You can log activity with your CASA youth, their family members, their foster family or placement, the DSS worker,
your Case Supervisor and others associated with your CASA case (such as teachers and therapists).
Your console account is associated with this email. If this is not the correct email to use, please stop here
and contact your Case Supervisor to change the email address. If you are ready to get started, please set your
password. This is the first step to accessing your new <%= @resource.type %> account.
<% else %>
A CASA console admin account has been created for you. Your console account is associated with this email.
If you are ready to get started, please set your password. This is the first step to accessing your new account.
<% end %>
<%= link_to "Set your password", accept_invitation_url(@resource, invitation_token: @token) %>
This invitation will expire on <%= I18n.l(@resource.invitation_due_at, format: :full, default: nil) %>
<% if @resource.is_a?(AllCasaAdmin) %>
(one week).
<% elsif @resource.is_a?(Supervisor) || @resource.is_a?(CasaAdmin) %>
(two weeks).
<% elsif @resource.is_a?(Volunteer) %>
(one year).
<% end %>
================================================
FILE: app/views/devise/mailer/invitation_instructions.text.erb
================================================
<% if @resource.is_a?(User) %>
A <%= @resource.casa_org.display_name %>’s County <%= @resource.type %> console account has been created for you. This console is for logging the time you spend and actions you take on your CASA case. You can log activity with your CASA youth, their family members, their foster family or placement, the DSS worker, your Case Supervisor and others associated with your CASA case (such as teachers and therapists).
Your console account is associated with this email. If this is not the correct email to use, please stop here and contact your Case Supervisor to change the email address. If you are ready to get started, please set your password. This is the first step to accessing your new <%= @resource.type %> account.
<% else %>
A CASA console admin account has been created for you. Your console account is associated with this email.
If you are ready to get started, please set your password. This is the first step to accessing your new account.
<%= "This invitation will be due in #{I18n.l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')}." %>
<% end %>
<%= link_to "Set your password", accept_invitation_url(@resource, invitation_token: @token) %>
================================================
FILE: app/views/devise/mailer/password_change.html.erb
================================================
Hello <%= @resource.email %>!
We're contacting you to notify you that your password has been changed.
================================================
FILE: app/views/devise/mailer/reset_password_instructions.html.erb
================================================
Actionable emails e.g. reset password
Court Appointed Special Advocate (CASA)
If you've lost your password or wish to reset it, click the button below. If
you didn’t request this, you can safely ignore this email.
Your password must be reset within the next <%= User.reset_password_within.inspect %>. If you do not
reset your password within the next <%= User.reset_password_within.inspect %>, request a new password
reset message from the login page.
================================================
FILE: app/views/fund_request_mailer/send_request.html.erb
================================================
Fund Request: see attached PDF
<%= form.label :youth_name, "Name or case number of youth" %>
<%= form.text_field :youth_name, class: "form-control", required: false, value: @casa_case&.case_number %>
<%= form.label :requested_by_and_relationship, "Requested by & relationship to youth" %>
<%= form.text_field :requested_by_and_relationship, class: "form-control", required: false,
value: "#{current_user.display_name} CASA Volunteer" %>
<%= form.label :other_funding_source_sought,
"Other source of funding available/sought please include status of these requests, if applicable." %>
<%= form.text_area :other_funding_source_sought, class: "form-control", required: false %>
<%= form.label :impact, "How will this funding positively impact the personal goals or aspirations of
the youth? If this is for emergency funding, please share any support that is
or can be in place to maintain stability or alleviate the emergency moving
forward. If funding is for a program or a service, please describe how this will
support the youth in the short or long-term." %>
<%= form.text_area :impact, class: "form-control", required: false %>
<%= form.label :extra_information,
"Please use this space if it is necessary/helpful to provide additional
information that will assist us in understanding the need and making a decision." %>
<%= form.text_area :extra_information, class: "form-control", required: false %>
<%= button_tag(type: "submit", class: "btn-sm main-btn primary-btn btn-hover", required: false) do %>
Submit Fund Request
<% end %>
<% end %>
* Please provide all documentation available for the requested expense (brochure,
receipt, estimate, invoice, proof of online enrollment information, etc.). If
additional funds have been secured, please provide evidence of the funding
commitment (an email, a partial payment on a bill, etc).
**Payment will be made directly to the vendor for all services. If the payment
must be made directly to the youth, please be sure to include why this is the
preferred method of payment.
<%= link_to edit_hearing_type_checklist_item_path(hearing_type, checklist_item) do %>
<% end %>
<%= link_to hearing_type_checklist_item_path(hearing_type, checklist_item),
method: :delete, data: { confirm: "Are you sure that you want to delete this checklist item?" } do %>
<% end %>
<% end %>
<% end %>
<%= button_tag(
type: "submit",
class: "btn-sm main-btn primary-btn btn-hover"
) do %>
Submit
<% end %>
Please check this box to verify that these mobile numbers have opted in to receive SMS notifications. (They will have the opportunity to opt out or update their preferences.)
<% end %>
================================================
FILE: app/views/layouts/_flash_messages.html.erb
================================================
<% flash.delete(:timedout).each do |key, value| %>
<%= sanitize(value, tags: %w(ul li)) %>
<% end %>
================================================
FILE: app/views/layouts/_header.html.erb
================================================
<%= render 'layouts/banner' %>
<% if current_user != true_user %>
<%= link_to stop_impersonating_volunteers_path, method: :post, class: "pt-4 pb-4 bg-danger", style: "padding-left: 15px; display: block;" do %>
You (<%= true_user.display_name %>) are signed in as <%= current_user.display_name %>.
Click here to stop impersonating.
<% end %>
<% end %>
<%= link_to notifications_path do %>
<% end %>
Role: <%= current_role %>
<%= current_user.email %>
<%= link_to edit_users_path do %>
Edit Profile
<% end %>
<%= link_to notifications_path do %>
Messages
<% end %>
<% if policy(:application).modify_organization? %>
<%= link_to edit_casa_org_path(current_organization) do %>
Edit Organization
<% end %>
<% end %>
<% help_url = current_user.volunteer? ? help_volunteers_url : help_admins_supervisors_url %>
<%= link_to help_url, target: :_blank do %>
Help
<% end %>
<%= link_to destroy_user_session_path do %>
Sign Out
<% end %>
<% if (@learning_hour.duration_hours > 0) %>
<%= @learning_hour.duration_hours %> hr
<% end %>
<%= @learning_hour.duration_minutes %> min
<%= link_to edit_learning_hour_path, class: "text-primary" do %>Edit<% end %> |
<%= link_to learning_hour_path, class: "text-danger", method: :delete,
data: { confirm: "Are you sure to delete this learning record?" } do %>Delete<% end %>
<%= form.text_area :content,
rows: 5,
placeholder: "Enter a note regarding the volunteer. These notes are only visible to CASA administrators and supervisors.",
class: "form-control" %>
<%= button_tag(
type: "submit",
class: "main-btn primary-btn btn-hover btn-sm",
id: "note-submit"
) do %>
Update Note
<% end %>
<% end %>
================================================
FILE: app/views/notifications/_notification.html.erb
================================================
<% if current_user.volunteer? %>
<%= link_to new_other_duty_path, class: "main-btn btn-sm primary-btn btn-hover" do %>
New Duty
<% end %>
<% end %>
<% if @volunteer_duties.empty? %>
There are no duties to display!
<% else %>
<% @volunteer_duties.each do |volunteer| %>
<% if volunteer[:other_duties].any? %>
<% unless current_user.id == volunteer[:volunteer].id %>
<%= link_to(volunteer[:volunteer].display_name, volunteer_path(volunteer[:volunteer].id)) %>
<% end %>
Duties Occurred
Created
Duration
Title
<% volunteer[:other_duties].decorate.each do |duty| %>
Supervisors and administrators can add cases and assign volunteers.
<%= image_tag("spreadsheets.svg", class: "app-images" , alt: "computer screen with graphs and charts" ) %>
Exportable Data
All CASA data is easily exportable in CSV format.
<%= image_tag("communicate.svg", class: "app-images" ,
alt: "woman holding a phone with a notification alert" ) %>
Communication
Easily communicate with volunteers by email, SMS or both.
<%= image_tag("quote.svg", class: "quote-image" , style: "width: 300px" ,
alt: "Quote: No one is useless in this world who lightens the burdens of another - Charles Dickens" ) %>
Testimonials
The tracker has helped us streamline our processes, and we no longer have to worry about losing track
of important documents.
- Sarah B. | Program Manager
Thanks to the tracker, we can now make quicker decisions and effectively
monitor our cases, resulting in a more pleasant experience for our volunteers.
Case <%= link_to casa_case.case_number, casa_case_url(casa_case) %>
<% successful_contacts = recently_unassigned ?
casa_case.decorate.successful_contacts_this_week_before(case_assignment.updated_at) :
casa_case.decorate.successful_contacts_this_week %>
<% unsuccessful_contacts = recently_unassigned ?
casa_case.decorate.unsuccessful_contacts_this_week_before(case_assignment.updated_at) :
casa_case.decorate.unsuccessful_contacts_this_week %>
<% if successful_contacts + unsuccessful_contacts > 0 %>
<%= "Number of successful case contacts made this week: #{successful_contacts}" %>
<%= "Number of unsuccessful case contacts made this week: #{unsuccessful_contacts} " %>
<% recent_contact = recently_unassigned ?
casa_case.decorate.case_contacts_latest_before(case_assignment.updated_at) :
casa_case.decorate.case_contacts_latest %>
<%= "Most recent contact attempted:" %>
<%= " - Date: #{I18n.l(recent_contact&.occurred_at, format: :full, default: nil)}" %>
<%= " - Type: #{recent_contact&.decorate.contact_types}" %>
<%= " - Duration: #{recent_contact&.duration_minutes}" %>
<%= " - Contact Made: #{recent_contact&.contact_made}" %>
<%= " - Medium Type: #{recent_contact&.medium_type}" %>
<% recent_contact.contact_topic_answers.reject { _1.value.blank? }.each do |answer| %>
- <%= "#{answer.contact_topic.question}" %><%= ": #{answer.value}" %>
<% end %>
<%= " - Notes: #{recent_contact&.notes}" %>
<% else %>
No contact attempts were logged for this week.
<% end %>
<% if recently_unassigned %>
This case was unassigned from <%= volunteer_display_name %> on
<%= case_assignment.updated_at.to_date.to_fs(:long_ordinal) %>
<% if successful_contacts + unsuccessful_contacts > 0 %>
The above activity only describes the part of the week when the case was still assigned to
<% volunteer_display_name %>
<% end %>
<% end %>
================================================
FILE: app/views/supervisor_mailer/_active_volunteers.html.erb
================================================
<% active_ever_assigned.each do |volunteer| %>
Summary for <%= link_to volunteer.display_name, edit_volunteer_url(volunteer) %>
<% end %>
================================================
FILE: app/views/supervisor_mailer/_additional_notes.html.erb
================================================
Additional Notes:
<% inactive_messages&.each do |message| %>
<%= message %>
<% end %>
<% if inactive_messages&.none? %>
There are no additional notes.
<% end %>
================================================
FILE: app/views/supervisor_mailer/_no_recent_sign_in.html.erb
================================================
<% if inactive_volunteers.any? %>
<%= "The following volunteers have not signed in or created case contacts in the last 30 days" %>
<% inactive_volunteers.each do |volunteer| %>
- <%= volunteer.display_name %>
<% end %>
<% end %>
================================================
FILE: app/views/supervisor_mailer/_pending_volunteers.html.erb
================================================
Pending Volunteers:
<% if @supervisor.pending_volunteers.empty? %>
There are no pending volunteers.
<% end %>
<% supervisor.pending_volunteers.each do |volunteer| %>
<%= volunteer.display_name %>
<% end %>
================================================
FILE: app/views/supervisor_mailer/_recently_unassigned_volunteers.html.erb
================================================
<% if supervisor.recently_unassigned_volunteers.any? %>
<%= "The following volunteers have been unassigned from you:" %>
<% supervisor.recently_unassigned_volunteers.each do |volunteer| %>
- <%= volunteer.display_name %>
<% unless volunteer.has_supervisor? %>
(not assigned to a new supervisor)
<% end %>
<% end %>
<% end %>
================================================
FILE: app/views/supervisor_mailer/_summary_header.html.erb
================================================
<%= supervisor_display_name %>,
<% if has_active_volunteers %>
You have no volunteers with assigned cases at the moment. When you do, you will see their status here.
<% else %>
Here's a summary of what happened with your volunteers this last week.
<% end %>
A <%= @supervisor.casa_org.display_name %>’s County supervisor console account has been created for you. This console is
for logging the time you spend and actions you take on your CASA case. You can log activity with your CASA youth,
their family members, their foster family or placement, the DSS worker, your Case Supervisor and others associated
with your CASA case (such as teachers and therapists).
Your console account is associated with this email. If this is not the correct email to use, please stop here and
contact your Administrator to change the email address. If you are ready to get started, please set your
password. This is the first step to accessing your new supervisor account.
<%= @volunteer.display_name %> has submitted a reimbursement request, please follow up on the reimbursements page
<%= link_to "using this link", reimbursements_url %>.
<% if user.active? %>
Supervisor is <%= render BadgeComponent.new(text: "Active", type: :success, rounded: true) %>
<% if current_user.casa_admin? %>
<% if policy(user).deactivate? %>
<%= link_to deactivate_supervisor_path(user), class: "btn-sm main-btn danger-btn-outline btn-hover", method: :patch, data: { confirm: "WARNING: Marking a supervisor inactive will make them unable to login. Are you sure you want to do this?" } do %>
Deactivate Supervisor
<% end %>
<% end %>
<% end %>
<% else %>
Supervisor was deactivated on: <%= user.decorate.formatted_updated_at %>
<% if policy(user).activate? %>
<%= link_to "Activate supervisor",
activate_supervisor_path(user),
method: :patch,
class: "btn-sm main-btn danger-btn-outline btn-hover" %>
<% end %>
<% end %>
<% if current_user.casa_admin? && user.invitation_accepted_at.nil? %>
<%= link_to resend_invitation_supervisor_path(user), class: "btn-sm main-btn danger-btn-outline btn-hover", method: :patch do %>
Resend Invitation
<% end %>
<% if current_user.casa_admin? %>
<%= link_to change_to_admin_supervisor_path(user), class: "btn-sm main-btn danger-btn-outline btn-hover", method: :patch do %>
Change to Admin
<% end %>
<% end %>
<% end %>
<%= form.button "Update Profile", type: "submit", class: "main-btn primary-btn btn-hover mb-3" %>
<% if policy(CasaAdmin).see_deactivate_option? %>
<% if @active_casa_admins.length > 1 %>
<%= link_to "Deactivate",
deactivate_casa_admin_path(current_user),
method: :patch,
class: "btn btn-outline-danger mb-3",
data: {confirm: "WARNING: Marking an admin inactive will make them unable to login. Are you sure you want to do this?"} %>
<% else %>
<%= link_to "Deactivate",
"#",
class: "main-btn danger-btn-outline btn-hover mb-3",
data: {confirm: "Contact your administrator at Ruby For Good to deactivate this account."} %>
<% end %>
<% end %>
A <%= @user.casa_org.display_name %>’s County volunteer console account has been created for you. This console is
for logging the time you spend and actions you take on your CASA case. You can log activity with your CASA youth,
their family members, their foster family or placement, the DSS worker, your Case Supervisor and others associated
with your CASA case (such as teachers and therapists).
Your console account is associated with this email. If this is not the correct email to use, please stop here and
contact your Case Supervisor to change the email address. If you are ready to get started, please set your
password. This is the first step to accessing your new volunteer account.
You are receiving this email as a reminder to input the case contacts which you have made. You can visit
" target="_blank">this link to edit your case contacts.
If you have any questions, please contact your most recent CASA supervisor for assistance.
This is a reminder that your next court report is due on <%= @court_report_due_date %>.
Please submit your court report to your supervisor no later than this date.
You can generate a court report by clicking on "Generate Court Reports" in your volunteer portal.
Log in at https://www.casavolunteertracking.org to get started.
Please email your supervisor directly if you need further assistance.
<%= button_tag(
type: "submit",
class: "main-btn primary-btn btn-hover btn-sm"
) do %>
Create Volunteer
<% end %>
<% end %>
================================================
FILE: app/views/volunteers/_manage_active.html.erb
================================================
<% if user.active? %>
Volunteer is <%= render BadgeComponent.new(text: "Active", type: :success) %>
<% if policy(user).deactivate? %>
<%= link_to deactivate_volunteer_path(user),
method: :patch,
class: "main-btn danger-btn-outline btn-hover btn-sm my-1",
data: {confirm: "WARNING: Marking a volunteer inactive will make them unable to login. Are you sure you want to do this?"} do %>
Deactivate volunteer
<% end %>
<% end %>
<% else %>
Volunteer was deactivated on: <%= user.decorate.formatted_updated_at %>
<% if policy(user).activate? %>
<%= link_to activate_volunteer_path(user),
method: :patch,
class: "main-btn success-btn-outline btn-hover btn-sm my-1" do %>
Activate volunteer
<% end %>
<% end %>
<% end %>
<% if (current_user.supervisor? ||
current_user.casa_admin?) &&
user.invitation_accepted_at.nil? %>
<%= link_to resend_invitation_volunteer_path(user),
class: "main-btn danger-btn-outline btn-hover btn-sm my-1" do %>
Resend Invitation (Email)
<% end %>
<% end %>
<% if current_user.casa_admin? %>
<%= link_to send_reactivation_alert_volunteer_path(user),
id: "#{current_user.casa_org.twilio_enabled? ? "twilio_enabled" : "twilio_disabled"}",
class: "main-btn danger-btn-outline btn-hover btn-sm my-1" do %>
<%= current_user.casa_org.twilio_enabled? ? "Send Reactivation Alert (SMS)" : "Enable Twilio To Send Reactivation Alert (SMS)" %>
<% end %>
<% end %>
<%= link_to edit_volunteer_note_path(@volunteer, note), class: "main-btn primary-btn btn-hover btn-sm" do %>
Edit
<% end %>
<%= link_to volunteer_note_path(@volunteer, note), class: "main-btn danger-btn btn-hover btn-sm", method: :delete do %>
Delete
<% end %>
<% end %>
<% end %>
<%= form_with(model: @volunteer.notes.new, local: true, url: volunteer_notes_path(@volunteer),
id: "volunteer-note-form") do |form| %>
Create a New Note
<%= form.text_area :content, :rows => 5, placeholder: "Enter a note regarding the volunteer. These notes are only visible to CASA administrators and supervisors.",
class: "form-control" %>
<%= button_tag(
type: "submit",
class: "main-btn primary-btn btn-hover btn-sm",
id: "note-submit"
) do %>
Save Note
<% end %>
<% if policy(current_organization.volunteers.new).new? %>
<%= link_to new_volunteer_path, class: "main-btn primary-btn btn-hover btn-sm mb-2 mb-md-0" do %>
New Volunteer
<% end %>
<% end %>
================================================
FILE: app.json
================================================
{
"name": "CASA",
"addons": [
"papertrail:choklad",
"scheduler:standard",
{
"plan": "heroku-postgresql",
"options": {
"version": "12"
}
}
],
"buildpacks": [
{
"url": "heroku/ruby"
}
],
"formation": {
"web": {
"quantity": 1,
"size": "hobby"
}
},
"scripts": {
"postdeploy": "bundle exec rake db:migrate db:seed"
},
"stack": "heroku-20"
}
================================================
FILE: babel.config.js
================================================
module.exports = { presets: ['@babel/preset-env'] }
================================================
FILE: bin/asset_bundling_scripts/build_js.js
================================================
#!/usr/bin/env node
const CLIArgs = process.argv.slice(2)
const isWatching = CLIArgs.includes('--watch')
const esbuild = require('esbuild')
const logger = require('./logger.js')
const watchingConsoleLogger = [{
name: 'watching-console-logger',
setup (build) {
build.onEnd(result => {
if (result.errors.length) {
logger.error('watch build failed:')
logger.error(` build failed with ${result.errors.length} errors`)
for (const error of result.errors) {
logger.error(' Error:')
logger.error(JSON.stringify(error, null, 2))
}
} else {
logger.info('watch build succeeded:')
logger.info(JSON.stringify(result, null, 2))
}
})
}
}]
async function main () {
const context = await esbuild.context({
entryPoints: ['app/javascript/application.js', 'app/javascript/all_casa_admin.js'],
outdir: 'app/assets/builds',
bundle: true,
plugins: watchingConsoleLogger
})
if (isWatching) {
await context.watch()
} else {
await context.rebuild()
await context.dispose()
}
}
main().catch((e) => {
console.error(e.message)
process.exit(1)
})
================================================
FILE: bin/asset_bundling_scripts/logger.js
================================================
const defaultText = '\x1b[0m'
// const bright = '\x1b[1m'
// const dim = '\x1b[2m'
// const underscore = '\x1b[4m'
// const blink = '\x1b[5m'
// const reverse = '\x1b[7m'
// const hidden = '\x1b[8m'
// const textBlack = '\x1b[30m'
const textRed = '\x1b[31m'
// const textGreen = '\x1b[32m'
const textYellow = '\x1b[33m'
// const textBlue = '\x1b[34m'
// const textMagenta = '\x1b[35m'
const textCyan = '\x1b[36m'
// const textWhite = '\x1b[37m'
// const highlightBlack = '\x1b[40m'
// const highlightRed = '\x1b[41m'
// const highlightGreen = '\x1b[42m'
// const highlightYellow = '\x1b[43m'
// const highlightBlue = '\x1b[44m'
// const highlightMagenta = '\x1b[45m'
// const highlightCyan = '\x1b[46m'
// const highlightWhite = '\x1b[47m'
module.exports = {
error: function (message) {
if (typeof message !== 'string') {
throw new TypeError('Param message must be a string')
}
console.error(textRed + message + defaultText)
},
info: function (message) {
if (typeof message !== 'string') {
throw new TypeError('Param message must be a string')
}
console.log(textCyan + message + defaultText)
},
warn: function (message) {
if (typeof message !== 'string') {
throw new TypeError('Param message must be a string')
}
console.warn(textYellow + message + defaultText)
}
}
================================================
FILE: bin/brakeman
================================================
#!/usr/bin/env ruby
require "rubygems"
require "bundler/setup"
ARGV.unshift("--ensure-latest")
load Gem.bin_path("brakeman", "brakeman")
================================================
FILE: bin/bundle
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'bundle' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
m = Module.new do
module_function
def invoked_as_script?
File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
end
def env_var_version
ENV['BUNDLER_VERSION']
end
def cli_arg_version
return unless invoked_as_script? # don't want to hijack other binstubs
unless 'update'.start_with?(ARGV.first || ' ')
return
end # must be running `bundle update`
bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
bundler_version = a
end
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
bundler_version = Regexp.last_match(1)
update_index = i
end
bundler_version
end
def gemfile
gemfile = ENV['BUNDLE_GEMFILE']
return gemfile if gemfile && !gemfile.empty?
File.expand_path('../Gemfile', __dir__)
end
def lockfile
lockfile =
case File.basename(gemfile)
when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
end
def lockfile_version
return unless File.file?(lockfile)
lockfile_contents = File.read(lockfile)
unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
return
end
Regexp.last_match(1)
end
def bundler_version
@bundler_version ||=
env_var_version || cli_arg_version ||
lockfile_version
end
def bundler_requirement
return "#{Gem::Requirement.default}.a" unless bundler_version
bundler_gem_version = Gem::Version.new(bundler_version)
requirement = bundler_gem_version.approximate_recommendation
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0')
requirement += '.a' if bundler_gem_version.prerelease?
requirement
end
def load_bundler!
ENV['BUNDLE_GEMFILE'] ||= gemfile
activate_bundler
end
def activate_bundler
gem_error = activation_error_handling do
gem 'bundler', bundler_requirement
end
return if gem_error.nil?
require_error = activation_error_handling do
require 'bundler/version'
end
if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
return
end
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
exit 42
end
def activation_error_handling
yield
nil
rescue StandardError, LoadError => e
e
end
end
m.load_bundler!
load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?
================================================
FILE: bin/delayed_job
================================================
#!/usr/bin/env ruby
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
require 'delayed/command'
Delayed::Command.new(ARGV).daemonize
================================================
FILE: bin/dev
================================================
#!/usr/bin/env bash
if ! command -v foreman &> /dev/null
then
echo "Installing foreman..."
gem install foreman
fi
foreman start -f Procfile.dev "$@"
================================================
FILE: bin/git_hooks/README.md
================================================
This folder contains helper scripts for [Git hooks](https://www.atlassian.com/git/tutorials/git-hooks).
To create a hook, make a file inside the directory `.git/hooks/` with the name of the hook you want to set up. The file names will **not** include an extension like `.md` or `.txt`.
For example, if you want to set up a pre-commit hook, the file should be called `pre-commit`, or if you want a pre-push hook, it should be called `pre-push`. Assuming you're on a unix based operating system, make sure the file is executable using `chmod +x `.
Once you've created that file, put the appropriate she-bang on the first line:
```bash
#!/bin/sh
```
followed by the script you want to run and any arguments or flags for that script.
See [Example Hooks](#example-hooks) below for how you might want to set these up.
You can read more about Git hooks [here](https://git-scm.com/docs/githooks).
## Hook Scripts
### `build-assets`
Compiles js and css to be served by your local webserver
Usage: `./build-assets`
### `lint`
Lints files on the current branch
Usage: `./lint `
+ ``(optional) can be one of the the following
- `--staged` lints the files staged for commit
- `--unpushed` lints files changed by commits not yet pushed to origin
- `--all` (default) lints all files in the repo
### `migrate-all`
Runs all migrations if any are found to be down
Usage: `./migrate-all`
### `update-dependences`
Installs dependencies if any are missing
Usage: `./update-dependencies`
### `update-branch`
Updates the `main` and current branch by rebasing your commits on top of changes from the official casa repo
This script assumes no commits were made directly to main
Usage: `./update-branch `
+ `` is the name of the [remote](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) pointing to the official casa repo
## Example Hooks
### pre-push
#!/bin/sh
./bin/git_hooks/update-branch actual_casa
./bin/git_hooks/lint --unpushed
### post-checkout
#!/bin/sh
./bin/git_hooks/update-dependencies
./bin/git_hooks/build-assets
### post-merge, post-rewrite
#!/bin/sh
./bin/git_hooks/update-dependencies
./bin/git_hooks/build-assets
# migrate-all has to come after update-dependencies because it relies on bundle
./bin/git_hooks/migrate-all
## Disabling Hook Scripts
If you ever need to disable a certain script from running in a hook, simply comment it out using `#`.
Example:
#!/bin/sh
./bin/git_hooks/update-branch actual_casa
#./bin/git_hooks/lint --unpushed # <-- This hook is now disabled
================================================
FILE: bin/git_hooks/build-assets
================================================
#!/bin/sh
# Builds assets(js and css)
# Usage:
# ./build-assets
repo_root="$(git rev-parse --show-toplevel)"
. "$repo_root/bin/git_hooks/logger"
log info "Building Assets"
if ! [ -x "$(command -v npm)" ]; then
log error "Command npm could not be found"
exit 1
fi
npm run build
npm run build:css
================================================
FILE: bin/git_hooks/lint
================================================
#!/bin/sh
# Lints files on the current branch
# Usage:
# lint
# --staged
# lints if the files staged for commit contain lintable files
# --unpushed
# lints files changed by commits not yet pushed to origin
# --all (default)
# lints all files in the repo
repo_root="$(git rev-parse --show-toplevel)"
. "$repo_root/bin/git_hooks/logger"
log info "Attempting to lint"
current_branch="$(git branch --show-current)"
if [ $# -lt 1 ]; then
diff_policy="--all"
else
diff_policy=$1
fi
case $diff_policy in
--all)
changed_file_list="app/models/a.rb\nb.erb\nc.js"
;;
--staged)
changed_file_list=$(git diff --name-only --cached)
;;
--unpushed)
if [ -z "$(git ls-remote --heads origin ${current_branch})" ]; then
changed_file_list=$(git diff --name-only HEAD~$(git cherry -v main ${current_branch} | wc -l) HEAD)
else
changed_file_list=$(git diff --name-only origin/${current_branch}..HEAD)
fi
;;
*)
log error "unknown option $diff_policy"
exit 1
;;
esac
erb_changed_count=$(echo "$changed_file_list" | grep -c "\.erb$")
factory_changed_count=$(echo "$changed_file_list" | grep -c -E "(app\/models|spec\/factories)\/.*\.rb$")
js_changed_count=$(echo "$changed_file_list" | grep -c "\.js$")
rb_changed_count=$(echo "$changed_file_list" | grep -c "\.rb$")
lint_time=$(date +%s)
if test $erb_changed_count -gt 0; then
log info "Linting via erblint"
cd $repo_root/app
if ! [ -x "$(command -v bundle)" ]; then
log error "Command bundle could not be found"
exit 1
else
if ! bundle exec erb_lint --lint-all --autocorrect ; then
log error "ERB Lint linting failed, could not fix 1 or more issues\n See above output for more details"
exit 1
fi
fi
fi
if test $factory_changed_count -gt 0; then
log info "Linting via factory:lint"
cd $repo_root/app
if ! [ -x "$(command -v bundle)" ]; then
log error "Command bundle could not be found"
exit 1
else
if ! bundle exec rake factory_bot:lint; then
log error "Factory bot linting failed, could not fix 1 or more issues\n See above output for more details"
exit 1
fi
fi
fi
if test $js_changed_count -gt 0; then
log info "Linting javasript via standard"
cd $repo_root/app
if ! [ -x "$(command -v npm)" ]; then
log error "Command npm could not be found"
exit 1
else
if ! npm run lint:fix ; then
log error "npm linting failed, could not fix 1 or more issues\n See above output for more details"
exit 1
fi
fi
fi
if test $rb_changed_count -gt 0; then
log info "Linting via standardrb"
cd $repo_root
if ! [ -x "$(command -v bundle)" ]; then
log error "Command bundle could not be found"
exit 1
else
if ! bundle exec standardrb --fix ; then
log error "Standard linting failed, could not fix 1 or more issues\n See above output for more details"
exit 1
fi
fi
fi
cd $repo_root
for file in $(git diff --name-only); do
last_modified_time=$(date -r $file +%s)
if [ $last_modified_time -ge $lint_time ] ; then
log warn "Some files were linted. Please preserve the changes before continuing.\n Run git diff to view linter changes."
exit 1
fi
done
log success "No files were linted"
================================================
FILE: bin/git_hooks/logger
================================================
#!/bin/sh
# Colorized output with logging levels
# Set colors if available
if test -t 1; then # if terminal
ncolors=$(which tput > /dev/null && tput colors) # supports color
if test -n "$ncolors" && test $ncolors -ge 8; then
normal="$(tput sgr0)"
red="$(tput setaf 1)"
green="$(tput setaf 2)"
yellow="$(tput setaf 3)"
cyan="$(tput setaf 6)"
fi
fi
# Colorized output
# Param $1 string | The logging level: info, warning, or error
# Param $2 string | The message to be logged
log () {
if [ $# -lt 2 ]; then
echo "${red}ERROR: function log was run with insufficient parameters ${normal}"
return
fi
case $1 in
success)
printf "${green}SUCCESS: $2 ${normal}\n"
;;
info)
printf "${cyan}INFO: $2 ${normal}\n"
;;
warn)
printf "${yellow}WARNING: $2 ${normal}\n"
;;
error)
printf "${red}ERROR: $2 ${normal}\n"
;;
*)
echo "${red}ERROR: Unrecognized log level: $1 ${normal}"
;;
esac
}
================================================
FILE: bin/git_hooks/migrate-all
================================================
#!/bin/sh
# Runs migrations if any are found to be down
# Usage:
# ./migrate-all
repo_root="$(git rev-parse --show-toplevel)"
. "$repo_root/bin/git_hooks/logger"
log info "Checking for down migrations"
if ! [ -x "$(command -v bundle)" ]; then
log error "Command bundle could not be found"
exit 1
fi
if bundle exec rails db:migrate:status | grep " down"; then
log info "Down migrations found. Migrating..."
bundle exec rails db:migrate
else
log success "No down migrations found"
fi
================================================
FILE: bin/git_hooks/update-branch
================================================
#!/bin/sh
# Updates the `main` and current branch by rebasing your commits on top of changes from the official casa repo
# Usage:
# update-branch
#
# The name of the remote pointing to the official casa repo
repo_root="$(git rev-parse --show-toplevel)"
. "$repo_root/bin/git_hooks/logger"
log info "Attempting to update local repo"
if [ $# -lt 1 ]; then
log error "Missing required arg "
exit 1
fi
upstream_remote=$1
branch_to_update="$(git branch --show-current)"
if test -z "$(git branch --list ${branch_to_update})"; then
log error "Could not find branch $branch_to_update"
exit 1
fi
log info "Fetching updates from upsteam"
git fetch $upstream_remote
log info "Updating main"
git checkout main
git merge --ff-only $upstream_remote/main
if test $branch_to_update != "main"; then
log info "Updating $branch_to_update"
git checkout $branch_to_update
git rebase -r $upstream_remote/main
fi
================================================
FILE: bin/git_hooks/update-dependencies
================================================
#!/bin/sh
# Installs dependencies if any are missing
# Usage:
# update-dependencies
# no arguments
repo_root="$(git rev-parse --show-toplevel)"
. "$repo_root/bin/git_hooks/logger"
log info "Checking rails dependency status"
if ! [ -x "$(command -v bundle)" ]; then
log error "Command bundle could not be found"
exit 1
fi
if ! [ -x "$(command -v npm)" ]; then
log error "Command npm could not be found"
exit 1
fi
if ! bundle check; then
log info "Updating rails dependencies"
bundle install
else
log success "Dependencies up to date"
fi
log info "Checking javascript dependency status"
if [ $(git diff HEAD@{1}..HEAD@{0} -- "package-lock.json" | wc -l) -gt 0 ] || [ $(git diff HEAD@{1}..HEAD@{0} -- "package.json" | wc -l) -gt 0 ]; then
log info "Updating JavaScript dependencies"
npm install
fi
================================================
FILE: bin/lint
================================================
#!/usr/bin/env bash
bundle exec standardrb --fix --format progress
bundle exec erb_lint --lint-all --autocorrect
npm run lint:fix
echo "Linting Factories"
rails factory_bot:lint
================================================
FILE: bin/login
================================================
#!/usr/bin/env ruby
# See HELP_TEXT below for description.
require 'webdrivers'
require 'capybara/dsl'
class LoginExecutor
include Capybara::DSL
Capybara.default_driver = :selenium
DEFAULT_LOGIN_URL = ENV['CASA_DEFAULT_LOGIN_URL'] || 'http://localhost:3000'
ALL_CASA_ADMIN_LOGIN_URL = ENV['ALL_CASA_ADMIN_LOGIN_URL'] || 'http://localhost:3000/all_casa_admins/sign_in'
User = Struct.new(:email, :url)
USERS = [
User.new('volunteer1@example.com', DEFAULT_LOGIN_URL),
User.new('supervisor1@example.com', DEFAULT_LOGIN_URL),
User.new('casa_admin1@example.com', DEFAULT_LOGIN_URL),
User.new('other_casa_admin@example.com', DEFAULT_LOGIN_URL),
User.new('other.supervisor@example.com', DEFAULT_LOGIN_URL),
User.new('other.volunteer@example.com', DEFAULT_LOGIN_URL),
User.new('allcasaadmin@example.com', ALL_CASA_ADMIN_LOGIN_URL),
]
HELP_TEXT = <<~HEREDOC
Usage: bin/login [user_number]
This script automates login for experimentation with the users added to the application when it is seeded
in development mode.
If executed without any arguments, it outputs the available users and accepts a numbered choice.
It then logs in as that choice, at the URL appropriate for that user.
It can also be executed with the user list element number passed as an argument, to bypass interactive mode.
The browser window remains open as long as the script has not yet terminated (using Ctrl-C).
You can either keep a terminal with this script open, or you can send it to the background with Ctrl-Z.
If the latter, when you are finished using the browser, you can bring the script back to the foreground with `fg[Enter]`.
HEREDOC
def self.login
self.new.call
end
def call
if ARGV.first == '-h'
puts HELP_TEXT
exit 0
end
user = ARGV.empty? ? get_user_from_input : get_user_from_arg
puts "\nLogging in to #{user.url} as #{user.email}...\n\n"
visit_and_log_in(user)
print_post_open_message_and_wait
end
private # ----------------------------- all methods below are private ----------------
def visit_and_log_in(user)
visit user.url
fill_in "Email", with: user.email
fill_in "Password", with: "12345678"
click_on "Log in"
end
def get_user_from_arg
arg = ARGV.first.strip
user = USERS[arg.to_i - 1]
unless user
puts "\nInvalid option: #{arg}. Must be a number between 1 and #{USERS.size}"
exit -1
end
user
end
def get_user_from_input
puts "With which user would you like to log in to CASA?\n\n"
USERS.each_with_index do |user, index|
puts "#{index + 1}) #{user.email}"
end
puts "\nInput a number, then [Enter]. An invalid entry will exit."
choice = $stdin.gets.chomp.to_i
exit unless (1..(USERS.size)).include?(choice)
USERS[choice - 1]
end
def print_post_open_message_and_wait
loop do
puts <<~HEREDOC
--------------------------------------------------------------------------------
Press Ctrl-C to exit and close browser.
To move this script to the background so you can continue using your terminal:
Press Ctrl-Z.
When you are done, type fg[Enter] and then Ctrl-C.
HEREDOC
if ARGV.empty?
puts "\nNext time you can pass the user number on the command line if you like.\n\n"
end
$stdin.gets
end
end
end
LoginExecutor.login
================================================
FILE: bin/npm
================================================
#!/usr/bin/env ruby
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
npm = ENV["PATH"].split(File::PATH_SEPARATOR).
select { |dir| File.expand_path(dir) != __dir__ }.
product(["npm", "npm.cmd", "npm.ps1"]).
map { |dir, file| File.expand_path(file, dir) }.
find { |file| File.executable?(file) }
if npm
command = ARGV.empty? ? ["install"] : ARGV
exec npm, *command
else
$stderr.puts "npm executable was not detected in the system."
$stderr.puts "Download npm by installing Node.js from https://nodejs.org/"
exit 1
end
end
================================================
FILE: bin/rails
================================================
#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
require_relative "../config/boot"
require "rails/commands"
================================================
FILE: bin/rake
================================================
#!/usr/bin/env ruby
require_relative "../config/boot"
require "rake"
Rake.application.run
================================================
FILE: bin/rspec
================================================
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
require 'bundler/setup'
load Gem.bin_path('rspec-core', 'rspec')
================================================
FILE: bin/setup
================================================
#!/usr/bin/env ruby
require 'fileutils'
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
def system!(*args)
system(*args, exception: true) || abort("\n== Command #{args} failed ==")
end
FileUtils.chdir APP_ROOT do
# This script is a way to set up or update your development environment automatically.
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
# Add necessary setup steps to this file.
expected_ruby_version = `cat .ruby-version`.chomp
current_ruby_version = `ruby -v`.chomp
unless current_ruby_version.include?(expected_ruby_version)
puts "Ruby version must be #{expected_ruby_version}. You are on #{current_ruby_version}"
exit
end
puts "\n== Installing dependencies =="
system! 'gem install foreman'
system! 'gem install bundler --conservative'
system!('bundle update --bundler --verbose')
system!('bundle check') || system!('bundle install')
# puts '\n== Copying sample files =='
# unless File.exist?('config/database.yml')
# FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
# end
unless File.exist?('.env')
puts "\n== Setup .env file from .env.example =="
system!('cp .env.example .env')
end
puts "\n== Preparing database =="
puts '⚠️ If you use docker to run postgres, make sure your database is running ⚠️'
system! 'bin/rails db:reset'
system! 'bin/rails db:test:prepare'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
puts "\n== Running post-deployment tasks =="
system! 'bin/rake after_party:run'
puts "\n== Installing npm packages =="
system!('npm ci') || abort('install npm and try again')
puts "\n== Building assets =="
system!('npm run build')
system!('npm run build:css')
puts "\n== Done =="
end
================================================
FILE: bin/spring
================================================
#!/usr/bin/env ruby
if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"])
gem "bundler"
require "bundler"
# Load Spring without loading other gems in the Gemfile, for speed.
Bundler.locked_gems&.specs&.find { |spec| spec.name == "spring" }&.tap do |spring|
Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
gem "spring", spring.version
require "spring/binstub"
rescue Gem::LoadError
# Ignore when Spring is not installed.
end
end
================================================
FILE: bin/update
================================================
#!/usr/bin/env ruby
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a way to update your development environment automatically.
# Add necessary update steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
puts 'Updating npm'
system('npm install') || abort("Install npm and try again")
puts "\n== Updating database =="
system! 'bin/rails db:migrate'
puts "\n== Running post-deployment tasks =="
system! 'bin/rake after_party:run'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
puts "\n== Building assets =="
system('npm run build') || abort("Failed to build assets. Ensure npm is installed and try again.")
system('npm run build:css') || abort("Failed to build CSS assets. Ensure npm is installed and try again.")
puts "\n== Done =="
end
================================================
FILE: cc-test-reporter
================================================
[File too large to display: 12.4 MB]
================================================
FILE: code-of-conduct.md
================================================
[View our Code of Conduct here!](./doc/code-of-conduct.md)
================================================
FILE: config/application.rb
================================================
require_relative "boot"
require "rails/all"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Casa
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.2
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets generators tasks mailers])
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
config.action_mailer.preview_paths << (defined?(Rails.root) ? Rails.root.join("lib/mailers/previews") : nil)
config.eager_load_paths << Rails.root.join("app/lib/importers")
config.assets.paths << Rails.root.join("app/assets/webfonts")
config.active_storage.variant_processor = :mini_magick
config.active_storage.content_types_to_serve_as_binary.delete("image/svg+xml")
config.serve_static_assets = true
# to use ViewComponent previews
config.view_component.previews.paths << "#{Rails.root.join("spec/components/previews")}"
end
end
================================================
FILE: config/boot.rb
================================================
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require "bundler/setup" # Set up gems listed in the Gemfile.
================================================
FILE: config/brakeman.ignore
================================================
{
"ignored_warnings": [
{
"warning_type": "Dynamic Render Path",
"warning_code": 15,
"fingerprint": "82ef033042422190ef49507207d51ed6ccd9593483630925baf0bf6c5e65033e",
"check_name": "Render",
"message": "Render path contains parameter value",
"file": "app/controllers/static_controller.rb",
"line": 21,
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
"code": "render(template => \"static/#{params[:name]}\", {})",
"render_path": null,
"location": {
"type": "method",
"class": "StaticController",
"method": "page"
},
"user_input": "params[:name]",
"confidence": "Medium",
"cwe_id": [
22
],
"note": ""
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "b75b292df5ec0c3d4d4f307a8ff2a18caecd456a9b3c9c62bb59d7cf3b67a562",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/datatables/supervisor_datatable.rb",
"line": 45,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "Arel.sql(\"COALESCE(users.display_name, users.email) #{order_direction}\")",
"render_path": null,
"location": {
"type": "method",
"class": "SupervisorDatatable",
"method": "order_clause"
},
"user_input": "order_direction",
"confidence": "Medium",
"cwe_id": [
89
],
"note": "We have a filter for this variable here: app/datatables/application_datatable.rbapp/datatables/application_datatable.rb:72"
}
],
"updated": "2023-01-17 23:08:51 -0500",
"brakeman_version": "5.4.0"
}
================================================
FILE: config/cable.yml
================================================
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: casa_production
================================================
FILE: config/credentials/development.key
================================================
f66bde702577ef605d3e69abc69798f8
================================================
FILE: config/credentials/development.yml.enc
================================================
aewvdbZoQz8v7s3UlJ/+XOIrxpj1/nP2/dA7FkLGvTgmu8lZrnyecC19sDE6bcZN4XsnIqDomjSg/CL8TefHKXOsaoNNKmW8YPVfoH8AmlqXxvJduiZNuXlOcf7SR01E7E0r1VIdRga6g9KtOHBbgtc6hQyOs/2ajSxbD3gY5IFWnWNHIqMEWMUMy/PXtSSxUr+FdNCgdod9Rx0EEiecfEz1tMBP/V69dRwSrM5yfTeogkUPpOqReFisTbn9f0yolmNhhxo7nPoPzyeEcGHl4+maS1GHa6uYQ2n2d2t34FmhcDttI+rV7ITU9LmuwVcjgCE9fPxMUZ9bX2UBUEHialBZ8S+izXyBAKGTvbQw+/Wk9KNT98Tl3Gg=--BRmMgMTOgyAZUyw4--2OyLty/a3xH0OjlI0sf9Yw==
================================================
FILE: config/credentials/production.yml.enc
================================================
Y85/mpXDwuasGg7odbPklWamaevc8nlJJExz5YTnGBR/S5wCGTkkH7hwUBPINNTkYXpe3N1QN2ILq+NuGqTJhgtpy6Glwnk743UlxsFUChUQmSlccJfML1YQWKx3R1VSAFWjOoHiV9vnA0RcKf9tIQ5FWY5S8/IMn4SlpRbhU0zJfkHAb3Hbx5juTWmOWHh4BD+AmKYqu9lpVCCwQkitRJX/8AwLUBprbcaHm0nh9u4PL/Q/Fj0/UadHF2RCWDnpRxUAygqgMpCBxqZwpHzN3cRrATL3aDYFZt0CZzpkpnHBKQcuSs1K/oki06Jas+Z585iO00S6r4NRJtkPVkznFqu0DJ8JkUKriLDXOwbvcEHOHtCS0t2hCxYLPZvSg51qBsZbgS5No3+cfXPeGjIecq+h1c7zpg70PNieO6290IM/jpNYA9ZEt9nPXwUodAauMkvcocXlOxX4mOaYsL7fMiXhgu/JzqwK64maSXpCBaEEPcOwIkaKhTwn+w+iABnTYnhZeD2t3TMoFpzkI83w7cg9Mbetm1N9faX43dsYXRW93tFwSkJtF4b//CIxOyG9xFxruGNwnBqy8+IuoaSyBVF3WecnWjj+7TGSgmgl7FmiaUSjDDJkh27K8QDHbpevQM/Rc5U=--8GtVk0JHu3Dkz4jI--/FAbx4kRZsAKWLg9/+IWbw==
================================================
FILE: config/credentials/qa.key
================================================
f66bde702577ef605d3e69abc69798f8
================================================
FILE: config/credentials/qa.yml.enc
================================================
kCD5s9jBDZZfXdKirYJD95LfwF3xeWi2E+OGS9x9/8EsI6WMzl+jNUjuBQt/fUdO5R5pCHXXPCYPzsGnDyTKJJczqszDXDsG2xS5eSImw9O7BGmNDh7W1sQ5kMmjZPUUu9GaCcAjmnvh0ImXM2X+Lo/5X3NeAZ7FllFmUd3CaBD5J3N3mbXlJgVGRylabaKABqrZqbRnZK5QDOcjwxYspsnu3m9M2qx5B7m2RnpKdytDpxbRtGSj34cZmH0C+rGOCQUm9rSR5wrQVmmJ5DdElYAnM7lo6bUTf2W5wbd51TsrEATPCHx1iK89ot/alUIjmHcoMlUstLnSX8VQzQeIluqa7a7o2IN/p++vknWELVlYROn67wiRTrLPXeDy+NkhiYkhQCBl3AIL8qs92tKX9xwvM75q5pGwsxQneGM5IjOSHUnEmw6j0f6B0R1oksOGt/xg9VP73ANP3zxqNTnbzRNoqO9wVVju0jJVt8KGoXVzdQx2qQSU5ZqUulJ7v88Xjro7Ew6mSY5gh4itzzsVIF7kAEJM20eh2WWxNEDMZyzvKDD34XWxOb8nks9MF0yCJzWBIog=--m9KJMwI1N3qSf0ry--d64JLH6L2BJNNIW2DdMR6w==
================================================
FILE: config/credentials/test.key
================================================
5dfb1ed8adf6c5264039101150e400ec
================================================
FILE: config/credentials/test.yml.enc
================================================
zsvjeQzFV0vM7h3jsx7RUyA3WTQsEYWvEMreEvVbi4L0HN7sDNrP7kay2FZS5VEwrW5mJZdnu63BXa8fK/h1agvaaiOO9FhDXfyK+VT86TAfsLa1gsBK9mHjWSdXCJE9TZj9OjOtR3R/qHOI+2uPUnysh7Om4a0ckiu4Jwex3OcbgCYj2+G2JQtwHkWhlyBthGxLjuDDFfx+qxkJWkN7V9FhN0FkkPaflyj4FjR9BUf3/CB8pvHXqJ1lmxVScYsyhh50mc+CKyVptpqbLi9Jou3SiUePREX03ynV0KPR+7mT3FH9gCj4QyzzS1t3JOUfrgqeVFAzdV1TW01olinOyG2aMrZn1aA7GWfDeIr/GwnaPfUMmZNj4RQ=--KmPWCR7xHr6jUSx5--1L2S0bUzD2Bc+JFAHX7xKg==
================================================
FILE: config/database.yml
================================================
default: &default
adapter: postgresql
encoding: unicode
username: <%= ENV.fetch("POSTGRES_USER") { } %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 3 } %>
port: <%= ENV.fetch("POSTGRES_PORT", "5432") %>
password: <%= ENV.fetch("POSTGRES_PASSWORD") { 'password' } %>
development:
<<: *default
database: casa_development
host: <%= ENV.fetch("DATABASE_HOST") { } %>
test:
<<: *default
database: casa_test<%= ENV["TEST_ENV_NUMBER"] %>
host: <%= ENV.fetch("DATABASE_HOST") { } %>
production:
<<: *default
database: casa_production
username: casa
password: <%= ENV['CASA_DATABASE_PASSWORD'] %>
================================================
FILE: config/docker.env
================================================
DOCKER=true
DATABASE_HOST=database
POSTGRES_USER=postgres
POSTGRES_HOST_AUTH_METHOD=trust
================================================
FILE: config/environment.rb
================================================
# Load the Rails application.
require_relative "application"
# Initialize the Rails application.
Rails.application.initialize!
================================================
FILE: config/environments/development.rb
================================================
require "active_support/core_ext/integer/time"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded any time
# it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.enable_reloading = true
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable server timing
config.server_timing = true
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :memory_store
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
config.cache_store = :null_store
end
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Don't care if the mailer can't send.
config.action_mailer.default_url_options = {host: "localhost", port: 3000}
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
config.action_mailer.perform_caching = false
config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise exceptions for disallowed deprecations.
config.active_support.disallowed_deprecation = :raise
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true
# Suppress logger output for asset requests.
config.assets.quiet = true
config.assets.digest = false
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
# Prosopite N+1 query detection
config.prosopite_enabled = true
config.prosopite_min_n_queries = 5 # More lenient for development
# Annotate rendered view with file names.
config.action_view.annotate_rendered_view_with_filenames = true
# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true
config.hosts << ENV["DEV_HOSTS"]
config.hosts << ".app.github.dev"
if ENV["CODESPACES"] === "true"
config.action_controller.forgery_protection_origin_check = false
end
# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = false
end
================================================
FILE: config/environments/production.rb
================================================
require "active_support/core_ext/integer/time"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
config.action_mailer.default_url_options = {host: ENV["DOMAIN"]}
config.action_mailer.raise_delivery_errors = true
config.action_mailer.show_previews = ENV["APP_ENVIRONMENT"] != "production"
# Do not send emails in staging or qa
config.action_mailer.perform_deliveries = ENV["APP_ENVIRONMENT"] == "production"
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp-relay.sendinblue.com",
port: 587,
user_name: ENV["SENDINBLUE_EMAIL"],
password: ENV["SENDINBLUE_PASSWORD"],
authentication: "login",
enable_starttls_auto: true
}
# Code is not reloaded between requests.
config.enable_reloading = false
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
# key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
config.require_master_key = true
# Enable static file serving from the `/public` folder (turn off if using NGINX/Apache for it).
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
# Compress CSS using a preprocessor.
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
# config.assets.compile = false
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.asset_host = "http://assets.example.com"
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
# config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :microsoft
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
# config.action_cable.url = "wss://example.com/cable"
# config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
# Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
# config.assume_ssl = true
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
# Log to STDOUT by default
config.logger = ActiveSupport::Logger.new($stdout)
.tap { |logger| logger.formatter = ::Logger::Formatter.new }
.then { |logger| ActiveSupport::TaggedLogging.new(logger) }
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
# Info include generic and useful information about system operation, but avoids logging too much
# information to avoid inadvertent exposure of personally identifiable information (PII). If you
# want to log everything, set the level to "debug".
config.log_level = :info
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Use a real queuing backend for Active Job (and separate queues per environment).
config.active_job.queue_adapter = :delayed_job # production.rb
# config.active_job.queue_name_prefix = "casa_production"
config.action_mailer.perform_caching = false
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Don't log any deprecations.
config.active_support.report_deprecations = false
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end
================================================
FILE: config/environments/test.rb
================================================
require "active_support/core_ext/integer/time"
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
config.action_mailer.default_url_options = {host: "localhost", port: 3000} # for devise authentication
# While tests run files are not watched, reloading is not necessary.
# Turn false under Spring and add config.action_view.cache_template_loading = true.
config.action_view.cache_template_loading = true
# Eager loading loads your entire application. When running a single test locally,
# this is usually not necessary, and can slow down your test suite. However, it's
# recommended that you enable it in continuous integration systems to ensure eager
# loading is working properly before deploying your code.
config.eager_load = ENV["CI"].present?
# cache classes on CI, but enable reloading for local work (bin/rspec)
config.enable_reloading = ENV["CI"].blank?
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{1.hour.to_i}"
}
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# config.cache_store = :null_store
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = :none
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
config.action_mailer.perform_caching = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
# Rack Attack configuration
# Set IP_BLOCKLIST for testing. Can't stub in spec since environment variable
# gets read during application initialization.
ENV["IP_BLOCKLIST"] = "4.5.6.7, 9.8.7.6,100.101.102.103"
# Raise exceptions for disallowed deprecations.
config.active_support.disallowed_deprecation = :raise
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []
# Raises error for missing translations.
config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = false
# https://github.com/rails/rails/issues/48468
config.active_job.queue_adapter = :test
config.secret_key_base = ENV["SECRET_KEY_BASE"] || "dummy_test_secret_key"
end
================================================
FILE: config/initializers/after_party.rb
================================================
AfterParty.setup do |config|
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require "after_party/active_record"
end
================================================
FILE: config/initializers/all_casa_admin_access.rb
================================================
class CanAccessFlipperUI
def self.matches?(request)
session = request.env["warden"].raw_session.to_h
session["warden.user.all_casa_admin.session"].present?
end
end
================================================
FILE: config/initializers/application_controller_renderer.rb
================================================
# Be sure to restart your server when you modify this file.
# ActiveSupport::Reloader.to_prepare do
# ApplicationController.renderer.defaults.merge!(
# http_host: 'example.org',
# https: false
# )
# end
================================================
FILE: config/initializers/assets.rb
================================================
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
================================================
FILE: config/initializers/authtrail.rb
================================================
# set to true for geocoding (and add the geocoder gem to your Gemfile)
# we recommend configuring local geocoding as well
# see https://github.com/ankane/authtrail#geocoding
AuthTrail.geocode = false
# add or modify data
# AuthTrail.transform_method = lambda do |data, request|
# data[:request_id] = request.request_id
# end
# exclude certain attempts from tracking
# AuthTrail.exclude_method = lambda do |data|
# data[:identity] == "capybara@example.org"
# end
================================================
FILE: config/initializers/backtrace_silencers.rb
================================================
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
# by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
# Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
================================================
FILE: config/initializers/blueprinter.rb
================================================
require "oj" # you can skip this if OJ has already been required.
Blueprinter.configure do |config|
config.generator = Oj # default is JSON
end
================================================
FILE: config/initializers/bugsnag.rb
================================================
if ENV["BUGSNAG_API_KEY"].present?
Bugsnag.configure do |config|
config.api_key = ENV["BUGSNAG_API_KEY"]
config.ignore_classes << ActiveRecord::RecordNotFound
config.release_stage = ENV["HEROKU_APP_NAME"] || ENV["APP_ENVIRONMENT"]
callback = proc do |event|
event.set_user(current_user&.id, current_user&.email) if defined?(current_user)
end
config.add_on_error(callback)
end
else
Bugsnag.configuration.logger = ::Logger.new("/dev/null")
end
================================================
FILE: config/initializers/content_security_policy.rb
================================================
# Be sure to restart your server when you modify this file.
# Define an application-wide content security policy.
# See the Securing Rails Applications Guide for more information:
# https://guides.rubyonrails.org/security.html#content-security-policy-header
# Rails.application.configure do
# config.content_security_policy do |policy|
# policy.default_src :self, :https
# policy.font_src :self, :https, :data
# policy.img_src :self, :https, :data
# policy.object_src :none
# policy.script_src :self, :https
# policy.style_src :self, :https
# # Specify URI for violation reports
# # policy.report_uri "/csp-violation-report-endpoint"
# end
#
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end
================================================
FILE: config/initializers/cookies_serializer.rb
================================================
# Be sure to restart your server when you modify this file.
# Specify a serializer for the signed and encrypted cookie jars.
# Valid options are :json, :marshal, and :hybrid.
Rails.application.config.action_dispatch.cookies_serializer = :json
================================================
FILE: config/initializers/cors.rb
================================================
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin Ajax requests.
# Read more: https://github.com/cyu/rack-cors
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "*" # make sure to change to domain name of frontend
resource "/api/v1/*", headers: :any, methods: [:get, :post, :patch, :put, :delete, :options, :head]
end
end
================================================
FILE: config/initializers/date_formats.rb
================================================
Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
Date::DATE_FORMATS[:slashes] = "%Y/%m/%d"
================================================
FILE: config/initializers/devise.rb
================================================
# frozen_string_literal: true
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# The secret key used by Devise. Devise uses this key to generate
# random tokens. Changing this key will render invalid all existing
# confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` as its `secret_key`
# by default. You can change it below and use your own secret key.
# config.secret_key = 'a42b150b56c158d6e064aa257c8434390c921811007f42c5aa16c7455b78e3777087c5a4bcf18bfb0886fdd9296dec34eaa02fc0b47bed58417347eba971ee2d'
# Fixes rspec triggering Rails.application.secrets deprecation warning for Rails 7.1.0 upgrade
config.secret_key = Rails.application.secret_key_base
# ==> Controller configuration
# Configure the parent class to the devise controllers.
# config.parent_controller = 'DeviseController'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = "no-reply@#{ENV["DOMAIN"]}"
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require "devise/orm/active_record"
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
# config.authentication_keys = [:email]
# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
# find_for_authentication method and considered in your model lookup. For instance,
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
# The same considerations mentioned for authentication_keys also apply to request_keys.
# config.request_keys = []
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [:email]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [:email]
# Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the
# given strategies, for example, `config.params_authenticatable = [:database]` will
# enable it only for database (email + password) authentication.
# config.params_authenticatable = true
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication. The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.
# config.http_authentication_realm = 'Application'
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing skip: :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth]
# By default, Devise cleans up the CSRF token on authentication to
# avoid CSRF token fixation attacks. This means that, when using AJAX
# requests for sign in and sign up, you need to get a new CSRF token
# from the server. You can disable this option at your own risk.
# config.clean_up_csrf_token_on_authentication = true
# When false, Devise will not attempt to reload routes on eager load.
# This can reduce the time taken to boot the app but if your application
# requires the Devise mappings to be loaded during boot time the application
# won't boot properly.
# config.reload_routes = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 11. If
# using other algorithms, it sets how many times you want the password to be hashed.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# algorithm), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
config.stretches = Rails.env.test? ? 1 : 11
# Set up a pepper to generate the hashed password.
# config.pepper = 'cd6eaac91bb902fefeb87724dc41ea5023881a0b3044bbf4c23178147d2a8d883265a58105ab7c549a7bb5cf3a732f253ab4a17f26e44eefaeacb37f2ee13278'
# Send a notification to the original email when the user's email is changed.
config.send_email_changed_notification = true
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
# ==> Configuration for :invitable
# The period the generated invitation token is valid.
# After this period, the invited resource won't be able to accept the invitation.
# When invite_for is 0 (the default), the invitation won't expire.
# config.invite_for = 2.weeks
# Number of invitations users can send.
# - If invitation_limit is nil, there is no limit for invitations, users can
# send unlimited invitations, invitation_limit column is not used.
# - If invitation_limit is 0, users can't send invitations by default.
# - If invitation_limit n > 0, users can send n invitations.
# You can change invitation_limit column for some users so they can send more
# or less invitations, even with global invitation_limit = 0
# Default: nil
# config.invitation_limit = 5
# The key to be used to check existing users when sending an invitation
# and the regexp used to test it when validate_on_invite is not set.
# config.invite_key = { email: /\A[^@]+@[^@]+\z/ }
# config.invite_key = { email: /\A[^@]+@[^@]+\z/, username: nil }
# Ensure that invited record is valid.
# The invitation won't be sent if this check fails.
# Default: false
# config.validate_on_invite = true
# Resend invitation if user with invited status is invited again
# Default: true
# config.resend_invitation = false
# The class name of the inviting model. If this is nil,
# the #invited_by association is declared to be polymorphic.
# Default: nil
# config.invited_by_class_name = 'User'
# The foreign key to the inviting model (if invited_by_class_name is set)
# Default: :invited_by_id
# config.invited_by_foreign_key = :invited_by_id
# The column name used for counter_cache column. If this is nil,
# the #invited_by association is declared without counter_cache.
# Default: nil
# config.invited_by_counter_cache = :invitations_count
# Auto-login after the user accepts the invite. If this is false,
# the user will need to manually log in after accepting the invite.
# Default: true
# config.allow_insecure_sign_in_after_accept = false
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
# able to access the website for two days without confirming their account,
# access will be blocked just in the third day.
# You can also set it to nil, which will allow the user to access the website
# without confirming their account.
# Default is 0.days, meaning the user cannot access the website without
# confirming their account.
# config.allow_unconfirmed_access_for = 2.days
# A period that the user is allowed to confirm their account before their
# token becomes invalid. For example, if set to 3.days, the user can confirm
# their account within 3 days after the mail was sent, but on the fourth day
# their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account.
# config.confirm_within = 3.days
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
config.reconfirmable = true
# Defines which key will be used when confirming an account
# config.confirmation_keys = [:email]
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
# Invalidates all the remember me tokens when the user signs out.
# config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
# config.rememberable_options = {}
# ==> Configuration for :validatable
# Range for password length.
config.password_length = 8..128
# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
config.timeout_in = 3.hours
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [:email]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
# config.maximum_attempts = 20
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# Warn on the last attempt before the account is locked.
# config.last_attempt_warning = true
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
# config.sign_in_after_reset_password = true
# ==> Configuration for :encryptable
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
# You can use :sha1, :sha512 or algorithms from others authentication tools as
# :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
# for default behavior) and :restful_authentication_sha1 (then you should set
# stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
#
# Require the `devise-encryptable` gem when using anything other than bcrypt
# config.encryptor = :sha512
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
config.scoped_views = true
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
config.default_scope = :user
# Set this configuration to false if you want /users/sign_out to sign out
# only the current scope. By default, Devise signs out all scopes.
# config.sign_out_all_scopes = true
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
config.navigational_formats = ["*/*", :html]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :get
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
# is mountable, there are some extra configurations to be taken into account.
# The following options are available, assuming the engine is mounted as:
#
# mount MyEngine, at: '/my_engine'
#
# The router that invoked `devise_for`, in the example above, would be:
# config.router_name = :my_engine
#
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
# ==> Turbolinks configuration
# If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
#
# ActiveSupport.on_load(:devise_failure_app) do
# include Turbolinks::Controller
# end
# ==> Configuration for :registerable
# When set to false, does not sign a user in automatically after their password is
# changed. Defaults to true, so a user is signed in automatically after changing a password.
# config.sign_in_after_change_password = true
end
================================================
FILE: config/initializers/extensions.rb
================================================
require "pdf_forms"
require "ext/pdf_forms"
================================================
FILE: config/initializers/filter_parameter_logging.rb
================================================
# Be sure to restart your server when you modify this file.
# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
# Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [
:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
]
================================================
FILE: config/initializers/flipper.rb
================================================
ActiveSupport.on_load(:active_record) do
require "flipper/adapters/active_record"
Flipper.configure do |config|
config.default do
adapter = Flipper::Adapters::ActiveRecord.new
Flipper.new(adapter)
end
end
end
================================================
FILE: config/initializers/generators.rb
================================================
# Allows rails generators (scaffold/controller) to use custom policy generator.
# Arguments for rails generators will be passed to the policy generator.
# Options will be shown in the help text for the rails generators,
# including the option to skip the policy generator (--skip-policy).
module PolicyGenerator
module ControllerGenerator
extend ActiveSupport::Concern
included do
hook_for :policy, in: nil, default: true, type: :boolean do |generator|
# use actions from controller invocation
invoke generator, [name.singularize, *actions]
end
end
end
module ScaffoldControllerGenerator
extend ActiveSupport::Concern
included do
hook_for :policy, in: nil, default: true, type: :boolean do |generator|
# prevent attribute arguments (name:string) being confused with actions
scaffold_actions = %w[index new create show edit update destroy]
invoke generator, [name.singularize, *scaffold_actions]
end
end
end
end
module ActiveModel
class Railtie < Rails::Railtie
generators do |app|
Rails::Generators.configure! app.config.generators
Rails::Generators::ControllerGenerator.include PolicyGenerator::ControllerGenerator
Rails::Generators::ScaffoldControllerGenerator.include PolicyGenerator::ScaffoldControllerGenerator
end
end
end
================================================
FILE: config/initializers/inflections.rb
================================================
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, "\\1en"
# inflect.singular /^(ox)en/i, "\\1"
# inflect.irregular "person", "people"
# inflect.uncountable %w( fish sheep )
# end
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym "RESTful"
# end
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "DSS" # TODO what is this for?
end
================================================
FILE: config/initializers/lograge.rb
================================================
Rails.application.configure do
config.lograge.enabled = true
end
================================================
FILE: config/initializers/mime_types.rb
================================================
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
Mime::Type.register "application/vnd.openxmlformats-officedocument.wordprocessingml.document", :docx
================================================
FILE: config/initializers/pagy.rb
================================================
require "pagy/extras/bootstrap"
require "pagy/extras/size"
Pagy::DEFAULT[:size] = [1, 3, 3, 1]
Pagy::DEFAULT[:nav_class] = "pagy-bootstrap-nav"
================================================
FILE: config/initializers/permissions_policy.rb
================================================
# Be sure to restart your server when you modify this file.
# Define an application-wide HTTP permissions policy. For further
# information see: https://developers.google.com/web/updates/2018/06/feature-policy
# Rails.application.config.permissions_policy do |policy|
# policy.camera :none
# policy.gyroscope :none
# policy.microphone :none
# policy.usb :none
# policy.fullscreen :self
# policy.payment :self, "https://secure.example.com"
# end
================================================
FILE: config/initializers/prosopite.rb
================================================
# frozen_string_literal: true
# Rack middleware for development only — in test, scanning is handled by RSpec hooks
if Rails.env.development? &&
Rails.configuration.respond_to?(:prosopite_enabled) &&
Rails.configuration.prosopite_enabled
require "prosopite/middleware/rack"
Rails.configuration.middleware.use(Prosopite::Middleware::Rack)
end
# Development configuration — test config lives in spec/support/prosopite.rb
Rails.application.config.after_initialize do
next unless Rails.env.development?
Prosopite.enabled = Rails.configuration.respond_to?(:prosopite_enabled) &&
Rails.configuration.prosopite_enabled
Prosopite.min_n_queries = Rails.configuration.respond_to?(:prosopite_min_n_queries) ?
Rails.configuration.prosopite_min_n_queries : 2
Prosopite.rails_logger = true
Prosopite.prosopite_logger = true
end
================================================
FILE: config/initializers/rack_attack.rb
================================================
class Rack::Attack
### Configure Cache ###
# If you don't want to use Rails.cache (Rack::Attack's default), then
# configure it here.
#
# Note: The store is only used for throttling (not blocklisting and
# safelisting). It must implement .increment and .write like
# ActiveSupport::Cache::Store
# Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
Rack::Attack.safelist("allow from localhost") do |req|
# Requests are allowed if the return value is truthy
req.ip == "127.0.0.1" || req.ip == "::1"
end
### Throttle Spammy Clients ###
# If any single client IP is making tons of requests, then they're
# probably malicious or a poorly-configured scraper. Either way, they
# don't deserve to hog all of the app server's CPU. Cut them off!
#
# Note: If you're serving assets through rack, those requests may be
# counted by rack-attack and this throttle may be activated too
# quickly. If so, enable the condition to exclude them from tracking.
# Throttle all requests by IP (60rpm)
#
# Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}"
throttle("req/ip", limit: 300, period: 5.minutes) do |req|
req.ip unless req.path.start_with?("/packs")
end
### Prevent Brute-Force Login Attacks ###
# The most common brute-force login attack is a brute-force password
# attack where an attacker simply tries a large number of emails and
# passwords to see if any credentials match.
#
# Another common method of attack is to use a swarm of computers with
# different IPs to try brute-forcing a password for a specific account.
# Throttle POST requests to /xxxx/sign_in by IP address
#
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}"
throttle("logins/ip", limit: 5, period: 20.seconds) do |req|
if req.path =~ /sign_in/ && req.post?
req.ip
end
end
throttle("reg/ip", limit: 5, period: 20.seconds) do |req|
req.ip if req.path.starts_with?("/api/v1")
end
# Throttle POST requests to /xxxx/sign_in by email param
#
# Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}"
#
# Note: This creates a problem where a malicious user could intentionally
# throttle logins for another user and force their login requests to be
# denied, but that's not very common and shouldn't happen to you. (Knock
# on wood!)
throttle("logins/email", limit: 5, period: 20.seconds) do |req|
if req.path =~ /sign_in/ && req.post?
# return the email if present, nil otherwise
req.params.dig("user", "email").presence ||
req.params.dig("all_casa_admin", "email").presence
end
end
Rack::Attack.blocklist("fail2ban pentesters") do |req|
# `filter` returns truthy value if request fails, or if it's from a
# previously banned IP so the request is blocked
Rack::Attack::Fail2Ban.filter("pentesters-#{req.ip}", maxretry: 3, findtime: 10.minutes, bantime: 1.day) do
# The count for the IP is incremented if the return value is truthy
CGI.unescape(req.query_string) =~ %r{/etc/passwd} ||
req.path.match?(/etc\/passwd/) ||
req.path.match(/wp-admin/i) ||
req.path.match(/wp-login/i) ||
req.path.match(/php/i) ||
req.path.match(/sql/i) ||
req.path.match(/PMA\d+/i) ||
req.path.match(/serverstatus/i) ||
req.path.match(/config\/server/i) ||
req.path.match(/xmlrpc/i) ||
req.path.match(/a2billing/i) ||
req.path.match(/testproxy/i) ||
req.path.match(/shopdb/i) ||
req.path.match(/index.action/i) ||
req.path.match(/etc\/services/i)
end
end
bad_ips = ENV["IP_BLOCKLIST"]
if bad_ips.present?
spammers = bad_ips.split(/\s*,\s*/)
spammer_regexp = Regexp.union(spammers)
blocklist("block bad ips") do |request|
request.ip =~ spammer_regexp
end
end
### Custom Throttle Response ###
# By default, Rack::Attack returns an HTTP 429 for throttled responses,
# which is just fine.
#
# If you want to return 503 so that the attacker might be fooled into
# believing that they've successfully broken your app (or you just want to
# customize the response), then uncomment these lines.
# self.throttled_response = lambda do |env|
# [ 503, # status
# {}, # headers
# ['']] # body
# end
end
================================================
FILE: config/initializers/rswag_api.rb
================================================
Rswag::Api.configure do |c|
# Specify a root folder where Swagger JSON files are located
# This is used by the Swagger middleware to serve requests for API descriptions
# NOTE: If you're using rswag-specs to generate Swagger, you'll need to ensure
# that it's configured to generate files in the same folder
c.openapi_root = Rails.root.to_s + "/swagger"
# Inject a lambda function to alter the returned Swagger prior to serialization
# The function will have access to the rack env for the current request
# For example, you could leverage this to dynamically assign the "host" property
#
# c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] }
end
================================================
FILE: config/initializers/rswag_ui.rb
================================================
Rswag::Ui.configure do |c|
# List the Swagger endpoints that you want to be documented through the
# swagger-ui. The first parameter is the path (absolute or relative to the UI
# host) to the corresponding endpoint and the second is a title that will be
# displayed in the document selector.
# NOTE: If you're using rspec-api to expose Swagger files
# (under swagger_root) as JSON or YAML endpoints, then the list below should
# correspond to the relative paths for those endpoints.
c.openapi_endpoint "/api-docs/v1/swagger.yaml", "API V1 Docs"
# Add Basic Auth in case your API is private
# c.basic_auth_enabled = true
# c.basic_auth_credentials 'username', 'password'
end
================================================
FILE: config/initializers/sent_email_event.rb
================================================
ActiveSupport::Notifications.subscribe "process.action_mailer" do |*args|
data = args.extract_options!
next if data[:mailer] == "DebugPreviewMailer"
user = data[:args][0]
if user&.role != "All Casa Admin"
SentEmail.create(
casa_org_id: user&.casa_org_id,
user_id: user&.id,
sent_address: user&.email,
mailer_type: data[:mailer],
category: data[:action].to_s.humanize
)
Rails.logger.info "#{data[:action]} email saved!"
end
end
================================================
FILE: config/initializers/strong_migrations.rb
================================================
# Mark existing migrations as safe
StrongMigrations.start_after = 20220117181508
# Set timeouts for migrations
# If you use PgBouncer in transaction mode, delete these lines and set timeouts on the database user
StrongMigrations.lock_timeout = 10.seconds
StrongMigrations.statement_timeout = 1.hour
# Analyze tables after indexes are added
# Outdated statistics can sometimes hurt performance
StrongMigrations.auto_analyze = true
# Set the version of the production database
# so the right checks are run in development
# StrongMigrations.target_version = 10
# Add custom checks
# StrongMigrations.add_check do |method, args|
# if method == :add_index && args[0].to_s == "users"
# stop! "No more indexes on the users table"
# end
# end
# Make some operations safe by default
# See https://github.com/ankane/strong_migrations#safe-by-default
# StrongMigrations.safe_by_default = true
================================================
FILE: config/initializers/wrap_parameters.rb
================================================
# Be sure to restart your server when you modify this file.
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = true
# end
================================================
FILE: config/locales/devise.en.yml
================================================
# Additional translations at https://github.com/plataformatec/devise/wiki/I18n
en:
devise:
confirmations:
confirmed: "Your email address has been successfully confirmed."
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
failure:
already_authenticated: "You are already signed in."
inactive: "Your account is currently inactive. Please contact your supervisor for more details."
invalid: "Invalid %{authentication_keys} or password."
locked: "Your account is locked."
last_attempt: "You have one more attempt before your account is locked."
not_found_in_database: "Invalid %{authentication_keys} or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in before continuing."
unconfirmed: "You have to confirm your email address before continuing."
mailer:
confirmation_instructions:
subject: "Confirmation instructions"
reset_password_instructions:
subject: "Reset password instructions"
unlock_instructions:
subject: "Unlock instructions"
email_changed:
subject: "Email Changed"
password_change:
subject: "Password Changed"
omniauth_callbacks:
failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
success: "Successfully authenticated from %{kind} account."
passwords:
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
updated: "Your password has been changed successfully. You are now signed in."
updated_not_active: "Your password has been changed successfully."
registrations:
destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
signed_up: "Welcome! You have signed up successfully."
signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
updated: "Your account has been updated successfully."
updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again"
sessions:
signed_in: "Signed in successfully."
signed_out: "Signed out successfully."
already_signed_out: "Signed out successfully."
unlocks:
send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
errors:
messages:
already_confirmed: "was already confirmed, please try signing in"
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
expired: "has expired, please request a new one"
not_found: "not found"
not_locked: "was not locked"
not_saved:
password: "%{count} error(s) prohibited this password change from being saved:"
one: "1 error prohibited this %{resource} from being saved:"
other: "%{count} errors prohibited this %{resource} from being saved:"
================================================
FILE: config/locales/devise_invitable.en.yml
================================================
en:
devise:
failure:
invited: >
"You have a pending invitation, accept it to finish creating your
account."
invitations:
send_instructions: "An invitation email has been sent to %{email}."
invitation_token_invalid: "The invitation token provided is not valid!"
updated: "Your password was set successfully. You are now signed in."
updated_not_active: "Your password was set successfully."
no_invitations_remaining: "No invitations remaining"
invitation_removed: "Your invitation was removed."
mailer:
invitation_instructions:
subject: "CASA Console invitation instructions"
time:
formats:
devise:
mailer:
invitation_instructions:
accept_until_format: "%B %d, %Y %I:%M %p"
================================================
FILE: config/locales/en.yml
================================================
# Files in the config/locales directory are used for internationalization
# and are automatically loaded by Rails. If you want to use locales other
# than English, add the necessary files in this directory.
#
# To use the locales, use `I18n.t`:
#
# I18n.t 'hello'
#
# In views, this is aliased to just `t`:
#
# <%= t('hello') %>
#
# To use a different locale, set it with `I18n.locale`:
#
# I18n.locale = :es
#
# This would use the information in config/locales/es.yml.
#
# The following keys must be escaped otherwise they will not be retrieved by
# the default I18n backend:
#
# true, false, on, off, yes, no
#
# Instead, surround them with single quotes.
#
# en:
# 'true': 'foo'
#
# To learn more, please read the Rails Internationalization guide
# available at https://guides.rubyonrails.org/i18n.html.
---
en:
activerecord:
attributes:
additional_expense:
other_expenses_amount: Amount
other_expenses_describe: Description
case_contact:
case_contact_contact_types:
one: Contact Type
other: Contact Types
contact_types:
one: Contact Type
other: Contact Types
contact_topic_answers:
one: Discussion Topic
other: Discussion Topics
draft_case_ids:
one: CASA Case
other: CASA Cases
occurred_at: Date
case_contact/additional_expenses:
other_expense_amount: Other Expense Amount
other_expenses_describe: Other Expense Details
case_contact/contact_topic_answers:
contact_topic: Discussion Topic
value: Discussion Notes
errors:
messages:
cant_be_future: can't be in the future
must_be_selected: must be selected
must_be_true_or_false: must be true or false
email_uniqueness: This email is already in use. If it does not appear on your roster, it may be associated with another CASA organization. Please use a different email address.
time:
formats:
day_and_date: "%A, %b %d, %Y"
standard: "%m-%d-%Y"
contact_occurred_at: "%Y%m%d%H%M%s"
full: "%B %-d, %Y"
youth_date_of_birth: "%B %Y"
short_date: "%-m/%d"
long_date: "%m/%d/%y"
edit_profile: "%B %d, %Y at %I:%M %p %Z"
time_on_date: "%-I:%-M %p on %m-%e-%Y"
date:
formats:
default: "%m/%d/%Y"
full: "%B %-d, %Y"
long: "%B %d, %Y"
short: "%b %d"
year_first: "%Y-%m-%d"
imports:
labels:
casa_case: "CASA Case"
volunteer: "Volunteer"
supervisor: "Supervisor"
================================================
FILE: config/puma.rb
================================================
# This configuration file will be evaluated by Puma. The top-level methods that
# are invoked here are part of Puma's configuration DSL. For more information
# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
# Puma starts a configurable number of processes (workers) and each process
# serves each request in a thread from an internal thread pool.
#
# The ideal number of threads per worker depends both on how much time the
# application spends waiting for IO operations and on how much you wish to
# to prioritize throughput over latency.
#
# As a rule of thumb, increasing the number of threads will increase how much
# traffic a given process can handle (throughput), but due to CRuby's
# Global VM Lock (GVL) it has diminishing returns and will degrade the
# response time (latency) of the application.
#
# The default is set to 3 threads as it's deemed a decent compromise between
# throughput and latency for the average Rails application.
#
# Any libraries that use a connection pool or another resource pool should
# be configured to provide at least as many connections as the number of
# threads. This includes Active Record's `pool` parameter in `database.yml`.
threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
port ENV.fetch("PORT", 3000)
# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
# In other environments, only set the PID file if requested.
pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
================================================
FILE: config/routes.rb
================================================
# frozen_string_literal: true
Rails.application.routes.draw do
mount Rswag::Ui::Engine => "/api-docs"
mount Rswag::Api::Engine => "/api-docs"
devise_for :all_casa_admins, path: "all_casa_admins", controllers: {sessions: "all_casa_admins/sessions"}
devise_for :users, controllers: {sessions: "users/sessions", passwords: "users/passwords", invitations: "users/invitations"}
authenticate :all_casa_admins do
mount PgHero::Engine, at: "pg_dashboard", constraints: lambda { |request|
admin = request.env["warden"].user(:all_casa_admin)
admin.present? && (admin.role == "All Casa Admin" || admin.casa_admin?)
}
end
concern :with_datatable do
post "datatable", on: :collection
end
authenticated :all_casa_admin do
root to: "all_casa_admins/dashboard#show", as: :authenticated_all_casa_admin_root
end
authenticated :user do
root to: "dashboard#show", as: :authenticated_user_root
end
devise_scope :user do
root to: "static#index"
end
devise_scope :all_casa_admins do
root to: "all_casa_admins/sessions#new", as: :unauthenticated_all_casa_root
end
resources :preference_sets, only: [] do
collection do
post "/table_state_update/:table_name", to: "preference_sets#table_state_update", as: :table_state_update
get "/table_state/:table_name", to: "preference_sets#table_state", as: :table_state
end
end
resources :health, only: %i[index] do
collection do
get :case_contacts_creation_times_in_last_week
get :gc
get :monthly_line_graph_data
get :monthly_unique_users_graph_data
end
end
get "/.well-known/assetlinks.json", to: "android_app_associations#index"
resources :casa_cases, except: %i[destroy] do
resource :emancipation, only: %i[show] do
member do
post "save"
end
end
resource :fund_request, only: %i[new create]
resources :court_dates, only: %i[create edit new show update destroy]
resources :placements
member do
patch :deactivate
patch :reactivate
patch :copy_court_orders
end
end
resources :casa_admins, except: %i[destroy show] do
member do
patch :deactivate
patch :activate
patch :resend_invitation
post :send_reactivation_alert
patch :change_to_supervisor
end
end
get "case_contacts/leave", to: "case_contacts#leave", as: "leave_case_contacts_form"
get "case_contacts/drafts", to: "case_contacts#drafts"
# Feature flag for new case contact table design
get "case_contacts/new_design", to: "case_contacts/case_contacts_new_design#index"
post "case_contacts/new_design/datatable", to: "case_contacts/case_contacts_new_design#datatable", as: "datatable_case_contacts_new_design"
resources :case_contacts, except: %i[create update show], concerns: %i[with_datatable] do
member do
post :restore
end
resources :form, controller: "case_contacts/form", only: %i[show update]
resources :followups, only: %i[create], controller: "case_contacts/followups", shallow: true do
patch :resolve, on: :member
end
end
resources :contact_topic_answers, only: %i[create destroy]
resources :reports, only: %i[index]
get :export_emails, to: "reports#export_emails"
resources :case_court_reports, only: %i[index show] do
collection do
post :generate
end
end
resources :reimbursements, only: %i[index change_complete_status], concerns: %i[with_datatable] do
patch :mark_as_complete, to: "reimbursements#change_complete_status"
patch :mark_as_needs_review, to: "reimbursements#change_complete_status"
end
resources :imports, only: %i[index create] do
collection do
get :download_failed
end
end
resources :case_contact_reports, only: %i[index]
resources :mileage_reports, only: %i[index]
resources :mileage_rates, only: %i[index new create edit update]
resources :casa_org, only: %i[edit update]
resources :contact_type_groups, only: %i[new create edit update]
resources :contact_types, only: %i[new create edit update]
resources :hearing_types, only: %i[new create edit update] do
resources :checklist_items, only: %i[new create edit update destroy]
end
resources :emancipation_checklists, only: %i[index]
resources :judges, only: %i[new create edit update]
resources :notifications, only: [:index] do
member do
post "mark_as_read"
end
end
resources :other_duties, only: %i[new create edit index update]
resources :missing_data_reports, only: %i[index]
resources :learning_hours_reports, only: %i[index]
resources :learning_hour_types, only: %i[new create edit update]
resources :learning_hour_topics, only: %i[new create edit update]
resources :placement_types, only: %i[new create edit update]
resources :contact_topics, except: %i[index show delete] do
delete "soft_delete", on: :member
end
resources :followup_reports, only: :index
resources :placement_reports, only: :index
resources :banners, except: %i[show] do
member do
get :dismiss
end
end
resources :bulk_court_dates, only: %i[new create]
resources :case_groups, only: %i[index new edit create update destroy]
resources :learning_hours, only: %i[index show new create edit update destroy]
namespace :learning_hours do
resources :volunteers, only: :show
end
resources :supervisors, except: %i[destroy show], concerns: %i[with_datatable] do
member do
patch :activate
patch :deactivate
patch :resend_invitation
patch :change_to_admin
end
end
resources :supervisor_volunteers, only: %i[create] do
collection do
post :bulk_assignment
end
member do
patch :unassign
end
end
resources :volunteers, except: %i[destroy], concerns: %i[with_datatable] do
post :stop_impersonating, on: :collection
member do
patch :activate
patch :deactivate
get :resend_invitation
get :send_reactivation_alert
patch :reminder
get :impersonate
end
resources :notes, only: %i[create edit update destroy]
end
resources :case_assignments, only: %i[create destroy] do
member do
get :unassign
patch :unassign
patch :show_hide_contacts
patch :reimbursement
end
end
resources :case_court_orders, only: %i[destroy]
resources :additional_expenses, only: %i[create destroy]
namespace :all_casa_admins do
resources :casa_orgs, only: [:new, :create, :show] do
resources :casa_admins, only: [:new, :create, :edit, :update] do
member do
patch :deactivate
patch :activate
end
end
end
resources :patch_notes, only: %i[create destroy index update]
end
resources :all_casa_admins, only: [:new, :create] do
collection do
get :edit
patch :update
patch "update_password"
end
end
resources :users, only: [] do
collection do
get :edit
patch :update
patch "update_password"
patch "update_email"
patch :add_language
delete :remove_language
end
end
resources :languages, only: %i[new create edit update] do
delete :remove_from_volunteer
end
resources :custom_org_links, only: %i[new create edit update destroy]
direct :help_admins_supervisors do
"https://thunder-flower-8c2.notion.site/Casa-Volunteer-Tracking-App-HelpSite-3b95705e80c742ffa729ccce7beeabfa"
end
direct :help_volunteers do
"https://thunder-flower-8c2.notion.site/Casa-Volunteer-Tracking-App-HelpSite-Volunteers-c24d9d2ef8b249bbbda8192191365039?pvs=4"
end
get "/error", to: "error#index"
post "/error", to: "error#create"
namespace :api do
namespace :v1 do
namespace :users do
post "sign_in", to: "sessions#create"
delete "sign_out", to: "sessions#destroy"
end
end
end
constraints CanAccessFlipperUI do
mount Flipper::UI.app(Flipper) => "/flipper"
end
end
================================================
FILE: config/scout_apm.yml
================================================
# This configuration file is used for Scout APM.
# https://scoutapm.com/docs/ruby/configuration
common: &defaults
# key: Your Organization key for Scout APM. Found on the settings screen.
# - Default: none
key: <%= ENV.fetch("SCOUT_APM_KEY", "local") %>
# log_level: Verboseness of logs.
# - Default: 'info'
# - Valid Options: debug, info, warn, error
# log_level: debug
# name: Application name in APM Web UI
# - Default: the application names comes from the Rails or Sinatra class name
# name:
# monitor: Enable Scout APM or not
# - Default: none
# - Valid Options: true, false
monitor: true
production:
<<: *defaults
errors_enabled: true
development:
<<: *defaults
monitor: false
dev_trace: true
test:
<<: *defaults
monitor: false
staging:
<<: *defaults
dev_trace: true
================================================
FILE: config/spring.rb
================================================
Spring.watch(
".ruby-version",
".rbenv-vars",
"tmp/restart.txt",
"tmp/caching-dev.txt"
)
================================================
FILE: config/storage.yml
================================================
test:
service: Disk
root: <%= Rails.root.join("tmp/storage#{ENV['TEST_ENV_NUMBER']}") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
microsoft:
service: AzureStorage
storage_account_name: <%= ENV['STORAGE_ACCOUNT_NAME'] %>
storage_access_key: <%= ENV['STORAGE_ACCESS_KEY'] %>
container: <%= ENV['STORAGE_CONTAINER'] %>
# mirror:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]
================================================
FILE: config.ru
================================================
# This file is used by Rack-based servers to start the application.
require_relative "config/environment"
run Rails.application
Rails.application.load_server
================================================
FILE: data/inputs_fdf.erb
================================================
%FDF-1.2
%âãÏÓ
1 0 obj
<
<< /V (<%= FdfInputsService.clean(value) %>) /T (<%= name %>) >>
<% end %>
]
>>
>>
endobj
trailer
<>
%%EOF
================================================
FILE: db/migrate/20200329050100_create_casa_cases.rb
================================================
class CreateCasaCases < ActiveRecord::Migration[6.0]
def change
create_table :casa_cases do |t|
t.string :case_number
t.boolean :teen_program_eligible
t.timestamps
end
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329062155_devise_create_users.rb
================================================
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.inet :current_sign_in_ip
# t.inet :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329064203_add_role_to_user.rb
================================================
class AddRoleToUser < ActiveRecord::Migration[6.0]
def change
add_column :users, :role, :string, null: false, default: "volunteer"
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329071025_change_casa_case_teen_to_required.rb
================================================
class ChangeCasaCaseTeenToRequired < ActiveRecord::Migration[6.0]
def change
change_column :casa_cases, :teen_program_eligible, :boolean, null: false, default: false
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329071327_change_casa_case_number_to_required.rb
================================================
class ChangeCasaCaseNumberToRequired < ActiveRecord::Migration[6.0]
def change
change_column :casa_cases, :case_number, :string, null: false
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329071626_add_unique_index_on_case_case_number.rb
================================================
class AddUniqueIndexOnCaseCaseNumber < ActiveRecord::Migration[6.0]
def change
add_index :casa_cases, :case_number, unique: true
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329074655_create_supervisor_volunteers.rb
================================================
class CreateSupervisorVolunteers < ActiveRecord::Migration[6.0]
def change
create_table :supervisor_volunteers do |t|
t.references :supervisor, foreign_key: {to_table: :users}, null: false
t.references :volunteer, foreign_key: {to_table: :users}, null: false
t.timestamps
end
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329081206_create_case_assignments.rb
================================================
class CreateCaseAssignments < ActiveRecord::Migration[6.0]
def change
create_table :case_assignments do |t|
t.references :casa_case, foreign_key: {to_table: :casa_cases}, null: false
t.references :volunteer, foreign_key: {to_table: :users}, null: false
t.boolean :is_active, null: false, default: true
t.timestamps
end
end
end
# rubocop:enable Style/Documentation
================================================
FILE: db/migrate/20200329085225_create_versions.rb
================================================
# This migration creates the `versions` table, the only schema PT requires.
# All other migrations PT provides are optional.
class CreateVersions < ActiveRecord::Migration[6.0]
# The largest text column available in all supported RDBMS is
# 1024^3 - 1 bytes, roughly one gibibyte. We specify a size
# so that MySQL will use `longtext` instead of `text`. Otherwise,
# when serializing very large objects, `text` might not be big enough.
TEXT_BYTES = 1_073_741_823
def change
create_table :versions do |t|
t.string :item_type, {null: false}
t.integer :item_id, null: false, limit: 8
t.string :event, null: false
t.string :whodunnit
t.text :object, limit: TEXT_BYTES
# Known issue in MySQL: fractional second precision
# -------------------------------------------------
#
# MySQL timestamp columns do not support fractional seconds unless
# defined with "fractional seconds precision". MySQL users should manually
# add fractional seconds precision to this migration, specifically, to
# the `created_at` column.
# (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)
#
# MySQL users should also upgrade to at least rails 4.2, which is the first
# version of ActiveRecord with support for fractional seconds in MySQL.
# (https://github.com/rails/rails/pull/14359)
#
t.datetime :created_at
end
add_index :versions, %i[item_type item_id]
end
end
================================================
FILE: db/migrate/20200329095031_create_casa_orgs.rb
================================================
class CreateCasaOrgs < ActiveRecord::Migration[6.0]
def change
create_table :casa_orgs do |t|
t.string :name, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20200329095154_add_casa_org_to_user.rb
================================================
class AddCasaOrgToUser < ActiveRecord::Migration[6.0]
def change
add_reference :users, :casa_org, foreign_key: true, null: false
end
end
================================================
FILE: db/migrate/20200329102102_devise_create_all_casa_admins.rb
================================================
# frozen_string_literal: true
class DeviseCreateAllCasaAdmins < ActiveRecord::Migration[6.0]
def change
create_table :all_casa_admins do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.inet :current_sign_in_ip
# t.inet :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :all_casa_admins, :email, unique: true
add_index :all_casa_admins, :reset_password_token, unique: true
# add_index :all_casa_admins, :confirmation_token, unique: true
# add_index :all_casa_admins, :unlock_token, unique: true
end
end
================================================
FILE: db/migrate/20200329175337_create_case_contacts.rb
================================================
class CreateCaseContacts < ActiveRecord::Migration[6.0]
def change
create_table :case_contacts do |t|
t.references :creator, foreign_key: {to_table: :users}, null: false
t.references :casa_case, null: false, foreign_key: true
t.string :contact_type, null: false
t.string :other_type_text, null: true
t.integer :duration_minutes, null: false
t.datetime :occurred_at, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20200330231711_add_volunteer_reference_to_casa_cases.rb
================================================
class AddVolunteerReferenceToCasaCases < ActiveRecord::Migration[6.0]
def change
add_reference :casa_cases, :volunteer
end
end
================================================
FILE: db/migrate/20200405112910_remove_volunteer_id_from_casa_case.rb
================================================
class RemoveVolunteerIdFromCasaCase < ActiveRecord::Migration[6.0]
def change
remove_reference :casa_cases, :volunteer
end
end
================================================
FILE: db/migrate/20200420004403_add_medium_type_and_contact_made_to_case_contacts.rb
================================================
class AddMediumTypeAndContactMadeToCaseContacts < ActiveRecord::Migration[6.0]
def change
add_column :case_contacts, :contact_made, :boolean, default: false
add_column :case_contacts, :medium_type, :string
end
end
================================================
FILE: db/migrate/20200422180727_replace_contact_type_with_contact_types_on_case_contact.rb
================================================
class ReplaceContactTypeWithContactTypesOnCaseContact < ActiveRecord::Migration[6.0]
def change
# NOTE: This is a destructive migration that we would normally avoid
# if we were working on production data, but because there is
# no production data we are comfortable being destructive and
# losting whatever data is in the `contact_type` column.
remove_column :case_contacts, :contact_type, :string
add_column :case_contacts, :contact_types, :string, array: true
# By default, indexes in postgresql are full-value indexes.
# However, when you have fields that hold multiple values, such as enums
# or jsonb, you want to rely on a full-text search index type.
# gin indexes are a full-text index type that works well in this context.
# You can read more at the official PostgreSQL docs:
# https://www.postgresql.org/docs/current/textsearch-indexes.html
add_index :case_contacts, :contact_types, using: :gin
end
end
================================================
FILE: db/migrate/20200423154018_change_teen_program_to_transition_aged_youth.rb
================================================
class ChangeTeenProgramToTransitionAgedYouth < ActiveRecord::Migration[6.0]
def change
rename_column :casa_cases, :teen_program_eligible, :transition_aged_youth
end
end
================================================
FILE: db/migrate/20200423204147_add_name_to_user.rb
================================================
class AddNameToUser < ActiveRecord::Migration[6.0]
def change
add_column :users, :display_name, :string, default: ""
end
end
================================================
FILE: db/migrate/20200525220759_add_driving_fields_to_case_contact.rb
================================================
class AddDrivingFieldsToCaseContact < ActiveRecord::Migration[6.0]
def up
add_column :case_contacts, :miles_driven, :integer, null: true
add_column :case_contacts, :want_driving_reimbursement, :boolean, default: false
execute <<-SQL
ALTER TABLE case_contacts
ADD CONSTRAINT want_driving_reimbursement_only_when_miles_driven
CHECK ((miles_driven IS NOT NULL) OR (NOT want_driving_reimbursement));
SQL
end
def down
execute <<-SQL
ALTER TABLE case_contacts
DROP CONSTRAINT want_driving_reimbursement_only_when_miles_driven
SQL
remove_column :case_contacts, :miles_driven, :integer
remove_column :case_contacts, :want_driving_reimbursement, :boolean
end
end
================================================
FILE: db/migrate/20200726185103_devise_invitable_add_to_users.rb
================================================
class DeviseInvitableAddToUsers < ActiveRecord::Migration[6.0]
def up
change_table :users do |t|
t.string :invitation_token
t.datetime :invitation_created_at
t.datetime :invitation_sent_at
t.datetime :invitation_accepted_at
t.integer :invitation_limit
t.references :invited_by, polymorphic: true
t.integer :invitations_count, default: 0
t.index :invitations_count
t.index :invitation_token, unique: true # for invitable
t.index :invited_by_id
end
end
def down
change_table :users do |t|
t.remove_references :invited_by, polymorphic: true
t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
end
end
end
================================================
FILE: db/migrate/20200729002247_add_casa_org_to_casa_case.rb
================================================
class AddCasaOrgToCasaCase < ActiveRecord::Migration[6.0]
def change
add_reference :casa_cases, :casa_org, null: false, foreign_key: true, index: true
end
end
================================================
FILE: db/migrate/20200801170524_add_type_to_user.rb
================================================
class User < ApplicationRecord
self.table_name = "users"
end
class AddTypeToUser < ActiveRecord::Migration[6.0]
def up
add_column :users, :type, :string
add_column :users, :active, :boolean, default: true
# Set supervisor
User.where(role: "supervisor").update_all(type: "Supervisor")
# Set volunteers
User.where(role: "volunteer").update_all(type: "Volunteer")
User.where(role: "inactive").update_all(type: "Volunteer", active: false)
# Set casa_admins
User.where(role: "casa_admin").update_all(type: "CasaAdmin")
end
def down
drop_column :users, :type
end
end
================================================
FILE: db/migrate/20200801192923_remove_role_from_user.rb
================================================
class RemoveRoleFromUser < ActiveRecord::Migration[6.0]
def change
remove_column :users, :role, :string, null: false, default: "volunteer"
end
end
================================================
FILE: db/migrate/20200818220659_add_is_active_to_supervisor_volunteers.rb
================================================
class AddIsActiveToSupervisorVolunteers < ActiveRecord::Migration[6.0]
def change
add_column :supervisor_volunteers, :is_active, :boolean, default: true
end
end
================================================
FILE: db/migrate/20200830011647_add_notes_to_case_contacts.rb
================================================
class AddNotesToCaseContacts < ActiveRecord::Migration[6.0]
def change
add_column :case_contacts, :notes, :string
end
end
================================================
FILE: db/migrate/20200905192934_remove_other_type_text_from_case_contacts.rb
================================================
class RemoveOtherTypeTextFromCaseContacts < ActiveRecord::Migration[6.0]
def change
remove_column :case_contacts, :other_type_text, :string
end
end
================================================
FILE: db/migrate/20200906145045_add_display_name_to_casa_orgs.rb
================================================
class AddDisplayNameToCasaOrgs < ActiveRecord::Migration[6.0]
def change
add_column :casa_orgs, :display_name, :string
end
end
================================================
FILE: db/migrate/20200906145725_add_address_to_casa_orgs.rb
================================================
class AddAddressToCasaOrgs < ActiveRecord::Migration[6.0]
def change
add_column :casa_orgs, :address, :string
end
end
================================================
FILE: db/migrate/20200906145830_add_footer_links_to_casa_orgs.rb
================================================
class AddFooterLinksToCasaOrgs < ActiveRecord::Migration[6.0]
def change
add_column :casa_orgs, :footer_links, :string, array: true, default: []
end
end
================================================
FILE: db/migrate/20200906150641_create_casa_org_logos.rb
================================================
class CreateCasaOrgLogos < ActiveRecord::Migration[6.0]
def change
create_table :casa_org_logos do |t|
t.references :casa_org, null: false, foreign_key: true
t.string :url
t.string :alt_text
t.string :size
t.timestamps
end
end
end
================================================
FILE: db/migrate/20200906184455_add_banner_color_to_casa_org_logos.rb
================================================
class AddBannerColorToCasaOrgLogos < ActiveRecord::Migration[6.0]
def change
add_column :casa_org_logos, :banner_color, :string
end
end
================================================
FILE: db/migrate/20200907142411_create_task_records.rb
================================================
class CreateTaskRecords < ActiveRecord::Migration[6.0]
def change
create_table :task_records, id: false do |t|
t.string :version, null: false
end
end
end
================================================
FILE: db/migrate/20200917175655_add_default_value_to_case_contact_miles_driven.rb
================================================
class AddDefaultValueToCaseContactMilesDriven < ActiveRecord::Migration[6.0]
def up
change_column :case_contacts, :miles_driven, :integer, default: 0
end
def down
change_column :case_contacts, :miles_driven, :integer, default: nil
end
end
================================================
FILE: db/migrate/20200918115741_set_miles_driven_as_not_nullable.rb
================================================
class SetMilesDrivenAsNotNullable < ActiveRecord::Migration[6.0]
def up
change_column :case_contacts, :miles_driven, :integer, null: false
end
def down
change_column :case_contacts, :miles_driven, :integer, null: true
end
end
================================================
FILE: db/migrate/20200922144730_add_birth_month_year_youth_to_casa_cases.rb
================================================
class AddBirthMonthYearYouthToCasaCases < ActiveRecord::Migration[6.0]
def change
add_column :casa_cases, :birth_month_year_youth, :datetime
end
end
================================================
FILE: db/migrate/20200922150754_create_contact_type.rb
================================================
class CreateContactType < ActiveRecord::Migration[6.0]
def change
create_table :contact_type_groups do |t|
t.references :casa_org, null: false
t.string :name, null: false
t.timestamps
end
create_table :contact_types do |t|
t.references :contact_type_group, null: false
t.string :name, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20200922170308_link_case_contacts_to_contact_types.rb
================================================
class LinkCaseContactsToContactTypes < ActiveRecord::Migration[6.0]
def change
create_table :case_contact_contact_types do |t|
t.references :case_contact, null: false
t.references :contact_type, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20200924192310_create_casa_case_contact_type.rb
================================================
class CreateCasaCaseContactType < ActiveRecord::Migration[6.0]
def change
create_table :casa_case_contact_types do |t|
t.references :contact_type, null: false
t.references :casa_case, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20200925180941_add_active_to_contact_type_groups.rb
================================================
class AddActiveToContactTypeGroups < ActiveRecord::Migration[6.0]
def change
add_column :contact_type_groups, :active, :boolean, default: true
end
end
================================================
FILE: db/migrate/20200925181042_add_active_to_contact_types.rb
================================================
class AddActiveToContactTypes < ActiveRecord::Migration[6.0]
def change
add_column :contact_types, :active, :boolean, default: true
end
end
================================================
FILE: db/migrate/20200928233606_add_court_report_submitted_to_casa_cases.rb
================================================
class AddCourtReportSubmittedToCasaCases < ActiveRecord::Migration[6.0]
def change
add_column :casa_cases, :court_report_submitted, :boolean, default: false, null: false
end
end
================================================
FILE: db/migrate/20201002192636_add_court_date_to_casa_cases.rb
================================================
class AddCourtDateToCasaCases < ActiveRecord::Migration[6.0]
def change
add_column :casa_cases, :court_date, :datetime
end
end
================================================
FILE: db/migrate/20201004165322_add_court_report_due_date_to_casa_cases.rb
================================================
class AddCourtReportDueDateToCasaCases < ActiveRecord::Migration[6.0]
def change
add_column :casa_cases, :court_report_due_date, :datetime
end
end
================================================
FILE: db/migrate/20201005191326_create_hearing_types.rb
================================================
class CreateHearingTypes < ActiveRecord::Migration[6.0]
def change
create_table :hearing_types do |t|
t.references :casa_org, null: false
t.string :name, null: false
t.boolean :active, null: false, default: true
end
end
end
================================================
FILE: db/migrate/20201013171632_remove_contact_types_from_case_contacts.rb
================================================
class RemoveContactTypesFromCaseContacts < ActiveRecord::Migration[6.0]
def change
# case_contacts.contact_types has been deprecated in favor of case_contact.case_contact_contact_type
remove_column :case_contacts, :contact_types, :string, array: true, default: []
end
end
================================================
FILE: db/migrate/20201019120548_create_active_storage_tables.active_storage.rb
================================================
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [:key], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [:record_type, :record_id, :name, :blob_id], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
================================================
FILE: db/migrate/20201020095451_add_hearing_type_to_court_cases.rb
================================================
class AddHearingTypeToCourtCases < ActiveRecord::Migration[6.0]
def change
add_reference :casa_cases, :hearing_type, null: true
end
end
================================================
FILE: db/migrate/20201020220412_create_emancipation_categories.rb
================================================
class CreateEmancipationCategories < ActiveRecord::Migration[6.0]
def change
create_table :emancipation_categories do |t|
t.string :name, null: false, index: {unique: true}
t.boolean :mutually_exclusive, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20201021024459_create_emancipation_options.rb
================================================
class CreateEmancipationOptions < ActiveRecord::Migration[6.0]
def change
create_table :emancipation_options do |t|
t.references :emancipation_category, null: false, foreign_key: true
t.string :name, null: false
t.index [:emancipation_category_id, :name], unique: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20201021034012_create_join_table_casa_cases_emancipaton_options.rb
================================================
class CreateJoinTableCasaCasesEmancipatonOptions < ActiveRecord::Migration[6.0]
def change
create_join_table :casa_cases, :emancipation_options do |t|
# Manually naming the index here because the autogenerated name has an illegal length
t.index [:casa_case_id, :emancipation_option_id], name: "index_cases_options_on_case_id_and_option_id", unique: true
end
end
end
================================================
FILE: db/migrate/20201021143642_add_active_column_to_casa_cases_table.rb
================================================
class AddActiveColumnToCasaCasesTable < ActiveRecord::Migration[6.0]
def change
add_column :casa_cases, :active, :boolean, default: true, null: false
end
end
================================================
FILE: db/migrate/20201022034445_add_foreign_key_constraints_to_case_emancipation_join_table.rb
================================================
class AddForeignKeyConstraintsToCaseEmancipationJoinTable < ActiveRecord::Migration[6.0]
def change
add_foreign_key :casa_cases_emancipation_options, :casa_cases
add_foreign_key :casa_cases_emancipation_options, :emancipation_options
end
end
================================================
FILE: db/migrate/20201023233638_create_judges.rb
================================================
class CreateJudges < ActiveRecord::Migration[6.0]
def change
create_table :judges do |t|
t.references :casa_org, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20201023234325_add_active_to_judge.rb
================================================
class AddActiveToJudge < ActiveRecord::Migration[6.0]
def change
add_column :judges, :active, :boolean, default: true
end
end
================================================
FILE: db/migrate/20201024003821_add_name_to_judge.rb
================================================
class AddNameToJudge < ActiveRecord::Migration[6.0]
def change
add_column :judges, :name, :string
end
end
================================================
FILE: db/migrate/20201024113046_create_past_court_dates.rb
================================================
class CreatePastCourtDates < ActiveRecord::Migration[6.0]
def change
create_table :past_court_dates do |t|
t.datetime :date, null: false
t.references :casa_case, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20201025162142_add_judge_to_court_cases.rb
================================================
class AddJudgeToCourtCases < ActiveRecord::Migration[6.0]
def change
add_reference :casa_cases, :judge, null: true
end
end
================================================
FILE: db/migrate/20201108142333_add_court_report_status_to_casa_cases.rb
================================================
class AddCourtReportStatusToCasaCases < ActiveRecord::Migration[6.0]
def change
add_column :casa_cases, :court_report_submitted_at, :datetime
add_column :casa_cases, :court_report_status, :integer, default: 0, not_null: true
end
end
================================================
FILE: db/migrate/20201120103041_remove_remember_created_at_from_users.rb
================================================
class RemoveRememberCreatedAtFromUsers < ActiveRecord::Migration[6.0]
def change
remove_column :users, :remember_created_at, :datetime
end
end
================================================
FILE: db/migrate/20201120103146_remove_remember_created_at_from_all_casa_admins.rb
================================================
class RemoveRememberCreatedAtFromAllCasaAdmins < ActiveRecord::Migration[6.0]
def change
remove_column :all_casa_admins, :remember_created_at, :datetime
end
end
================================================
FILE: db/migrate/20201120215756_remove_court_report_submitted_from_casa_cases.rb
================================================
class RemoveCourtReportSubmittedFromCasaCases < ActiveRecord::Migration[6.0]
def change
remove_column :casa_cases, :court_report_submitted, :boolean
end
end
================================================
FILE: db/migrate/20201123100716_remove_case_number_index_from_casa_cases.rb
================================================
class RemoveCaseNumberIndexFromCasaCases < ActiveRecord::Migration[6.0]
def change
remove_index :casa_cases, column: :case_number, unique: true
end
end
================================================
FILE: db/migrate/20201123112651_add_case_number_index_scoped_by_casa_org_to_casa_cases.rb
================================================
class AddCaseNumberIndexScopedByCasaOrgToCasaCases < ActiveRecord::Migration[6.0]
def change
add_index :casa_cases, [:case_number, :casa_org_id], unique: true
end
end
================================================
FILE: db/migrate/20201222125441_add_service_name_to_active_storage_blobs.active_storage.rb
================================================
# This migration comes from active_storage (originally 20190112182829)
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
def up
unless column_exists?(:active_storage_blobs, :service_name)
add_column :active_storage_blobs, :service_name, :string
if (configured_service = ActiveStorage::Blob.service.name)
ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
end
change_column :active_storage_blobs, :service_name, :string, null: false
end
end
def down
remove_column :active_storage_blobs, :service_name
end
end
================================================
FILE: db/migrate/20201222125442_create_active_storage_variant_records.active_storage.rb
================================================
# This migration comes from active_storage (originally 20191206030411)
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
def change
create_table :active_storage_variant_records do |t|
t.belongs_to :blob, null: false, index: false
t.string :variation_digest, null: false
t.index %i[blob_id variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
================================================
FILE: db/migrate/20201226024029_create_casa_case_emancipation_categories.rb
================================================
class CreateCasaCaseEmancipationCategories < ActiveRecord::Migration[6.1]
def change
create_table :casa_case_emancipation_categories do |t|
t.references :casa_case, null: false, foreign_key: true
t.references :emancipation_category, null: false, foreign_key: true, index: {name: "index_case_emancipation_categories_on_emancipation_category_id"}
t.timestamps
end
end
end
================================================
FILE: db/migrate/20210105155534_create_followups.rb
================================================
class CreateFollowups < ActiveRecord::Migration[6.1]
def change
create_table :followups do |t|
t.belongs_to :case_contact
t.belongs_to :creator, foreign_key: {to_table: :users}
t.integer :status, default: 0, not_null: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20210107181908_add_id_and_timestamps_to_case_emancipation_options_table.rb
================================================
class AddIdAndTimestampsToCaseEmancipationOptionsTable < ActiveRecord::Migration[6.1]
def change
add_column :casa_cases_emancipation_options, :id, :primary_key
add_timestamps :casa_cases_emancipation_options, default: Time.zone.now
change_column_default :casa_cases_emancipation_options, :created_at, nil
change_column_default :casa_cases_emancipation_options, :updated_at, nil
end
end
================================================
FILE: db/migrate/20210109231411_drop_casa_org_logos_table.rb
================================================
class DropCasaOrgLogosTable < ActiveRecord::Migration[6.1]
def up
drop_table :casa_org_logos
end
def down
fail ActiveRecord::IrreversibleMigration
end
end
================================================
FILE: db/migrate/20210117185614_create_notifications.rb
================================================
class CreateNotifications < ActiveRecord::Migration[6.1]
def change
create_table :notifications do |t|
t.references :recipient, polymorphic: true, null: false
t.string :type, null: false
t.jsonb :params
t.datetime :read_at
t.timestamps
end
add_index :notifications, :read_at
end
end
================================================
FILE: db/migrate/20210223133248_create_case_court_mandates.rb
================================================
class CreateCaseCourtMandates < ActiveRecord::Migration[6.1]
def change
create_table :case_court_mandates do |t|
t.string :mandate_text
t.references :casa_case, foreign_key: true, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20210308195135_change_is_active_name.rb
================================================
class ChangeIsActiveName < ActiveRecord::Migration[6.1]
def change
rename_column :case_assignments, :is_active, :active
end
end
================================================
FILE: db/migrate/20210330182538_add_devise_trackable_columns_to_users.rb
================================================
class AddDeviseTrackableColumnsToUsers < ActiveRecord::Migration[6.1]
def change
add_column :users, :sign_in_count, :integer, default: 0, null: false
add_column :users, :current_sign_in_at, :datetime
add_column :users, :last_sign_in_at, :datetime
add_column :users, :current_sign_in_ip, :string
add_column :users, :last_sign_in_ip, :string
end
end
================================================
FILE: db/migrate/20210401161710_add_deleted_at_to_case_contacts.rb
================================================
class AddDeletedAtToCaseContacts < ActiveRecord::Migration[6.1]
def change
add_column :case_contacts, :deleted_at, :datetime
add_index :case_contacts, :deleted_at
end
end
================================================
FILE: db/migrate/20210401182359_add_implementation_status_to_edit_case_court_mandates.rb
================================================
class AddImplementationStatusToEditCaseCourtMandates < ActiveRecord::Migration[6.1]
def change
add_column :case_court_mandates, :implementation_status, :integer
end
end
================================================
FILE: db/migrate/20210502172706_add_devise_invitable_to_all_casa_admins.rb
================================================
class AddDeviseInvitableToAllCasaAdmins < ActiveRecord::Migration[6.1]
def change
add_column :all_casa_admins, :invitation_token, :string
add_column :all_casa_admins, :invitation_created_at, :datetime
add_column :all_casa_admins, :invitation_sent_at, :datetime
add_column :all_casa_admins, :invitation_accepted_at, :datetime
add_column :all_casa_admins, :invitation_limit, :integer
add_column :all_casa_admins, :invited_by_id, :integer
add_column :all_casa_admins, :invited_by_type, :string
add_index :all_casa_admins, :invitation_token, unique: true
end
end
================================================
FILE: db/migrate/20210521151549_add_casa_case_details_to_past_court_dates.rb
================================================
class AddCasaCaseDetailsToPastCourtDates < ActiveRecord::Migration[6.1]
def change
add_reference :past_court_dates, :hearing_type, null: true
add_reference :past_court_dates, :judge, null: true
end
end
================================================
FILE: db/migrate/20210521194321_add_past_court_date_to_case_court_mandates.rb
================================================
class AddPastCourtDateToCaseCourtMandates < ActiveRecord::Migration[6.1]
def change
add_reference :case_court_mandates, :past_court_date, null: true
end
end
================================================
FILE: db/migrate/20210526233058_create_sent_emails.rb
================================================
class CreateSentEmails < ActiveRecord::Migration[6.1]
def change
create_table :sent_emails do |t|
t.belongs_to :user, index: true, foreign_key: true
t.references :casa_org, null: false, foreign_key: true
t.string :mailer_type
t.string :category
t.string :sent_address
t.timestamps
end
end
end
================================================
FILE: db/migrate/20210624125750_add_note_to_followups.rb
================================================
class AddNoteToFollowups < ActiveRecord::Migration[6.1]
def change
add_column :followups, :note, :text
end
end
================================================
FILE: db/migrate/20210913142024_set_display_name_as_not_nullable.rb
================================================
class SetDisplayNameAsNotNullable < ActiveRecord::Migration[6.1]
def up
change_column :users, :display_name, :string, null: false
end
def down
change_column :users, :display_name, :string, null: true
end
end
================================================
FILE: db/migrate/20210925140028_add_slug_to_casa_orgs_and_casa_cases.rb
================================================
class AddSlugToCasaOrgsAndCasaCases < ActiveRecord::Migration[6.1]
def change
add_column :casa_cases, :slug, :string
add_column :casa_orgs, :slug, :string
add_index :casa_cases, :slug
add_index :casa_orgs, :slug, unique: true
end
end
================================================
FILE: db/migrate/20211001204053_rename_court_mandates_to_court_orders.rb
================================================
class RenameCourtMandatesToCourtOrders < ActiveRecord::Migration[6.1]
def change
rename_table :case_court_mandates, :case_court_orders
end
end
================================================
FILE: db/migrate/20211007144114_add_show_driving_reimbursement_to_casa_orgs.rb
================================================
class AddShowDrivingReimbursementToCasaOrgs < ActiveRecord::Migration[6.1]
def change
add_column :casa_orgs, :show_driving_reimbursement, :boolean, default: true
end
end
================================================
FILE: db/migrate/20211008170357_add_text_to_case_court_orders.rb
================================================
class AddTextToCaseCourtOrders < ActiveRecord::Migration[6.1]
def change
add_column :case_court_orders, :text, :string
end
end
================================================
FILE: db/migrate/20211008170724_migrate_case_court_orders_mandate_text_to_text.rb
================================================
class MigrateCaseCourtOrdersMandateTextToText < ActiveRecord::Migration[6.1]
def up
execute "update case_court_orders set text=mandate_text"
end
end
================================================
FILE: db/migrate/20211008174527_remove_mandate_text_from_case_court_orders.rb
================================================
class RemoveMandateTextFromCaseCourtOrders < ActiveRecord::Migration[6.1]
def change
remove_column :case_court_orders, :mandate_text, :string
end
end
================================================
FILE: db/migrate/20211011195857_rename_past_court_date_to_court_date.rb
================================================
class RenamePastCourtDateToCourtDate < ActiveRecord::Migration[6.1]
def change
rename_table :past_court_dates, :court_dates
rename_column :case_court_orders, :past_court_date_id, :court_date_id
end
end
================================================
FILE: db/migrate/20211012180102_change_casa_cases_court_date_to_reference.rb
================================================
class ChangeCasaCasesCourtDateToReference < ActiveRecord::Migration[6.1]
def up
CasaCase.find_each do |casa_case|
CourtDate.create(
date: casa_case.court_date,
casa_case: casa_case,
hearing_type: casa_case.hearing_type,
judge: casa_case.judge,
case_court_orders: casa_case.case_court_orders
)
end
end
end
================================================
FILE: db/migrate/20211023165907_add_reimbursement_complete_to_case_contacts.rb
================================================
class AddReimbursementCompleteToCaseContacts < ActiveRecord::Migration[6.1]
def change
add_column :case_contacts, :reimbursement_complete, :boolean, default: false
end
end
================================================
FILE: db/migrate/20211024011923_create_mileage_rates.rb
================================================
class CreateMileageRates < ActiveRecord::Migration[6.1]
def change
create_table :mileage_rates do |t|
t.decimal :amount
t.date :effective_date
t.boolean :is_active, default: true
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20211024060815_create_preference_sets.rb
================================================
class CreatePreferenceSets < ActiveRecord::Migration[6.1]
def change
create_table :preference_sets do |t|
t.references :user, index: true, foreign_key: true
t.jsonb :case_volunteer_columns, null: false, default: "{}"
t.timestamps
end
end
end
================================================
FILE: db/migrate/20211025143709_create_healths.rb
================================================
class CreateHealths < ActiveRecord::Migration[6.1]
def change
create_table :healths do |t|
t.timestamp :latest_deploy_time
t.integer :singleton_guard
t.timestamps
end
add_index(:healths, :singleton_guard, unique: true)
end
end
================================================
FILE: db/migrate/20211029032305_add_casa_org_to_mileage_rate.rb
================================================
class AddCasaOrgToMileageRate < ActiveRecord::Migration[6.1]
def change
# null false is dangerous if there are any in the db already! There aren't tho
add_column :mileage_rates, :casa_org_id, :bigint, null: false
add_index :mileage_rates, :casa_org_id
end
end
================================================
FILE: db/migrate/20211029033530_remove_user_required_from_mileage_rate.rb
================================================
class RemoveUserRequiredFromMileageRate < ActiveRecord::Migration[6.1]
def change
change_column_null :mileage_rates, :user_id, true
end
end
================================================
FILE: db/migrate/20211203181342_create_additional_expenses.rb
================================================
class CreateAdditionalExpenses < ActiveRecord::Migration[6.1]
def change
create_table :additional_expenses do |t|
t.references :case_contact, null: false, foreign_key: true
t.decimal "other_expense_amount"
t.string "other_expenses_describe"
t.timestamps
end
end
end
================================================
FILE: db/migrate/20211230033457_create_delayed_jobs.rb
================================================
class CreateDelayedJobs < ActiveRecord::Migration[6.1]
def self.up
create_table :delayed_jobs do |table|
table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
table.text :handler, null: false # YAML-encoded string of the object that will do work
table.text :last_error # reason for last failure (See Note below)
table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
table.datetime :locked_at # Set when a client is working on this object
table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
table.string :locked_by # Who is working on this object (if locked)
table.string :queue # The name of the queue this job is in
table.timestamps null: true
end
add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
end
def self.down
drop_table :delayed_jobs
end
end
================================================
FILE: db/migrate/20220105030922_create_feature_flags.rb
================================================
class CreateFeatureFlags < ActiveRecord::Migration[6.1]
def change
create_table :feature_flags do |t|
t.string :name, null: false
t.boolean :enabled, null: false, default: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220127055733_create_notes.rb
================================================
class CreateNotes < ActiveRecord::Migration[6.1]
def change
create_table :notes do |t|
t.string :content
t.bigint :creator_id
t.references :notable, polymorphic: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220223035901_remove_null_check_deprecated_field.rb
================================================
class RemoveNullCheckDeprecatedField < ActiveRecord::Migration[6.1]
def change
change_column_null :casa_cases, :transition_aged_youth, true
end
end
================================================
FILE: db/migrate/20220226040507_create_fund_request.rb
================================================
class CreateFundRequest < ActiveRecord::Migration[6.1]
def change
create_table :fund_requests do |t|
t.text :submitter_email
t.text :youth_name
t.text :payment_amount
t.text :deadline
t.text :request_purpose
t.text :payee_name
t.text :requested_by_and_relationship
t.text :other_funding_source_sought
t.text :impact
t.text :extra_information
t.text :timestamps
end
end
end
================================================
FILE: db/migrate/20220303183053_create_other_duty.rb
================================================
class CreateOtherDuty < ActiveRecord::Migration[6.1]
def change
create_table :other_duties do |t|
t.bigint :creator_id, null: false
t.string :creator_type
t.datetime :occurred_at
t.bigint :duration_minutes
t.text :notes
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220323145733_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
================================================
# This migration comes from active_storage (originally 20211119233751)
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
def change
change_column_null(:active_storage_blobs, :checksum, true)
end
end
================================================
FILE: db/migrate/20220324141758_create_learning_hours.rb
================================================
class CreateLearningHours < ActiveRecord::Migration[6.1]
def change
create_table :learning_hours do |t|
t.references :user, null: false, foreign_key: true
t.integer :learning_type, default: 5, not_null: true
t.string :name, null: false
t.integer :duration_minutes, null: false
t.integer :duration_hours, null: false
t.datetime :occurred_at, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220402201247_add_phone_number_to_users.rb
================================================
class AddPhoneNumberToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :phone_number, :string, default: ""
end
end
================================================
FILE: db/migrate/20220406011016_add_sms_notification_preferences_to_users.rb
================================================
class AddSmsNotificationPreferencesToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :receive_sms_notifications, :boolean, null: false, default: false
end
end
================================================
FILE: db/migrate/20220406011144_add_email_notification_preferences_to_users.rb
================================================
class AddEmailNotificationPreferencesToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :receive_email_notifications, :boolean, default: true
end
end
================================================
FILE: db/migrate/20220409184741_add_fund_request.rb
================================================
class AddFundRequest < ActiveRecord::Migration[7.0]
def change
add_column :casa_orgs, :show_fund_request, :boolean, default: false
end
end
================================================
FILE: db/migrate/20220411180242_add_uniqueness_constraint_to_feature_flag_name.rb
================================================
class AddUniquenessConstraintToFeatureFlagName < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_index :feature_flags, :name, unique: true, algorithm: :concurrently
end
end
================================================
FILE: db/migrate/20220509224425_add_columns_to_casa_orgs.rb
================================================
class AddColumnsToCasaOrgs < ActiveRecord::Migration[7.0]
def change
add_column :casa_orgs, :twilio_phone_number, :string
add_column :casa_orgs, :twilio_account_sid, :string
add_column :casa_orgs, :twilio_api_key_sid, :string
add_column :casa_orgs, :twilio_api_key_secret, :string
end
end
================================================
FILE: db/migrate/20220513084954_add_date_in_care_to_casa_cases.rb
================================================
class AddDateInCareToCasaCases < ActiveRecord::Migration[7.0]
def change
add_column :casa_cases, :date_in_care, :datetime
end
end
================================================
FILE: db/migrate/20220513111133_delete_versions.rb
================================================
class DeleteVersions < ActiveRecord::Migration[7.0]
def up
drop_table :versions
end
# copied from db/migrate/20200329085225_create_versions.rb
TEXT_BYTES = 1_073_741_823
def down
create_table :versions do |t|
t.string :item_type, {null: false}
t.integer :item_id, null: false, limit: 8
t.string :event, null: false
t.string :whodunnit
t.text :object, limit: TEXT_BYTES
# Known issue in MySQL: fractional second precision
# -------------------------------------------------
#
# MySQL timestamp columns do not support fractional seconds unless
# defined with "fractional seconds precision". MySQL users should manually
# add fractional seconds precision to this migration, specifically, to
# the `created_at` column.
# (https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html)
#
# MySQL users should also upgrade to at least rails 4.2, which is the first
# version of ActiveRecord with support for fractional seconds in MySQL.
# (https://github.com/rails/rails/pull/14359)
#
t.datetime :created_at
end
add_index :versions, %i[item_type item_id]
end
end
================================================
FILE: db/migrate/20220519210423_create_sms_notification_events.rb
================================================
class CreateSmsNotificationEvents < ActiveRecord::Migration[7.0]
def change
create_table :sms_notification_events do |t|
t.string :name
t.string :user_type
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220519233803_create_user_sms_notification_events.rb
================================================
class CreateUserSmsNotificationEvents < ActiveRecord::Migration[7.0]
def change
create_table :user_sms_notification_events do |t|
t.references :user, null: false, foreign_key: true
t.references :sms_notification_event, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220526011848_create_user_reminder_times.rb
================================================
class CreateUserReminderTimes < ActiveRecord::Migration[7.0]
def change
create_table :user_reminder_times do |t|
t.belongs_to :user, null: false, foreign_key: true
t.datetime :case_contact_types
t.datetime :no_contact_made
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220602215632_create_addresses.rb
================================================
class CreateAddresses < ActiveRecord::Migration[7.0]
def change
create_table :addresses do |t|
t.string :content
t.belongs_to :user, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220607184910_add_hide_old_contacts_to_case_assignment.rb
================================================
class AddHideOldContactsToCaseAssignment < ActiveRecord::Migration[7.0]
def change
add_column :case_assignments, :hide_old_contacts, :boolean, default: false
end
end
================================================
FILE: db/migrate/20220610221701_create_checklist_items.rb
================================================
class CreateChecklistItems < ActiveRecord::Migration[7.0]
def change
create_table :checklist_items do |t|
t.integer :hearing_type_id
t.text :description, null: false
t.string :category, null: false
t.boolean :mandatory, default: false, null: false
t.timestamps
end
add_index :checklist_items, :hearing_type_id
end
end
================================================
FILE: db/migrate/20220615015056_create_patch_note_types.rb
================================================
class CreatePatchNoteTypes < ActiveRecord::Migration[7.0]
def change
create_table :patch_note_types do |t|
t.string :name, null: false
t.index :name, unique: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220616021404_add_checklist_updated_date_to_hearing_types.rb
================================================
class AddChecklistUpdatedDateToHearingTypes < ActiveRecord::Migration[7.0]
def change
add_column :hearing_types, :checklist_updated_date, :string, default: "None", null: false
end
end
================================================
FILE: db/migrate/20220618042137_create_patch_note_groups.rb
================================================
class CreatePatchNoteGroups < ActiveRecord::Migration[7.0]
def change
create_table :patch_note_groups do |t|
t.string :value, null: false
t.index :value, unique: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220622022147_create_patch_notes.rb
================================================
class CreatePatchNotes < ActiveRecord::Migration[7.0]
def change
create_table :patch_notes do |t|
t.text :note, null: false
t.references :patch_note_type, null: false, foreign_key: true
t.references :patch_note_group, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220820231119_create_languages.rb
================================================
class CreateLanguages < ActiveRecord::Migration[7.0]
def change
create_table :languages do |t|
t.string :name
t.references :casa_org, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20220826130829_create_languages_users_join_table.rb
================================================
class CreateLanguagesUsersJoinTable < ActiveRecord::Migration[7.0]
def change
create_join_table :languages, :users do |t|
t.index :language_id
t.index :user_id
end
end
end
================================================
FILE: db/migrate/20220924181447_remove_all_empty_languages.rb
================================================
class RemoveAllEmptyLanguages < ActiveRecord::Migration[7.0]
def up
safety_assured { execute "DELETE from languages WHERE name IS NULL or trim(name) = ''" }
end
end
================================================
FILE: db/migrate/20221002103627_add_user_foreign_key_to_other_duties.rb
================================================
class AddUserForeignKeyToOtherDuties < ActiveRecord::Migration[7.0]
def change
add_foreign_key :other_duties, :users, column: :creator_id, validate: false
end
end
================================================
FILE: db/migrate/20221002103754_validate_add_user_foreign_key_to_other_duties.rb
================================================
class ValidateAddUserForeignKeyToOtherDuties < ActiveRecord::Migration[7.0]
def change
validate_foreign_key :other_duties, :users
end
end
================================================
FILE: db/migrate/20221003202112_add_court_report_due_date_to_court_dates.rb
================================================
class AddCourtReportDueDateToCourtDates < ActiveRecord::Migration[7.0]
def change
add_column :court_dates, :court_report_due_date, :datetime, precision: nil
end
end
================================================
FILE: db/migrate/20221011044911_add_user_languages.rb
================================================
class AddUserLanguages < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
create_table :user_languages do |t|
t.references :user
t.references :language
t.timestamps
end
add_index :user_languages, [:language_id, :user_id], unique: true, algorithm: :concurrently
end
end
================================================
FILE: db/migrate/20221012203806_populate_user_languages_from_languages_users.rb
================================================
class PopulateUserLanguagesFromLanguagesUsers < ActiveRecord::Migration[7.0]
def change
query = Arel.sql("select language_id, user_id from languages_users")
old_join_table_entries = ActiveRecord::Base.connection.execute(query).to_a
old_join_table_entries.each do |entry|
UserLanguage.create(user_id: entry["user_id"], language_id: entry["language_id"])
end
end
end
================================================
FILE: db/migrate/20230121174227_remove_court_data_from_casa_cases.rb
================================================
class RemoveCourtDataFromCasaCases < ActiveRecord::Migration[7.0]
def change
safety_assured { remove_column :casa_cases, :court_date }
end
end
================================================
FILE: db/migrate/20230220210146_add_phone_number_to_all_casa_admin_resource.rb
================================================
class AddPhoneNumberToAllCasaAdminResource < ActiveRecord::Migration[7.0]
def change
add_column :all_casa_admins, :phone_number, :string, default: ""
end
end
================================================
FILE: db/migrate/20230316152808_remove_phone_number_from_all_casa_admin_resource.rb
================================================
class RemovePhoneNumberFromAllCasaAdminResource < ActiveRecord::Migration[7.0]
def change
safety_assured { remove_column :all_casa_admins, :phone_number }
end
end
================================================
FILE: db/migrate/20230326225216_create_placement_types.rb
================================================
class CreatePlacementTypes < ActiveRecord::Migration[7.0]
def change
create_table :placement_types do |t|
t.string :name, null: false
t.references :casa_org, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230326225230_create_placements.rb
================================================
class CreatePlacements < ActiveRecord::Migration[7.0]
def change
create_table :placements do |t|
# Add table placement_type: name, casa_org_id. Add table placement: placement_id, casa_case_id, started_at, created_by_id (links to user table)
t.datetime :placement_started_at, null: false
t.references :placement_type, null: false, foreign_key: true
t.references :creator, foreign_key: {to_table: :users}, null: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230327154626_add_confirmable_to_users.rb
================================================
class AddConfirmableToUsers < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
# Note: You can't use change, as User.update_all will fail in the down migration
def up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
add_index :users, :confirmation_token, unique: true, algorithm: :concurrently
# User.reset_column_information # Need for some types of updates, but not for update_all.
# To avoid a short time window between running the migration and updating all existing
# users as confirmed, do the following
# User.update_all confirmed_at: DateTime.now
# All existing user accounts should be able to log in after this.
end
def down
remove_index :users, :confirmation_token
remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
remove_columns :users, :unconfirmed_email # Only if using reconfirmable
end
end
================================================
FILE: db/migrate/20230327155053_add_email_confirmation_and_old_emails_to_users.rb
================================================
class AddEmailConfirmationAndOldEmailsToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :old_emails, :string, array: true, default: []
add_column :users, :email_confirmation, :string
end
end
================================================
FILE: db/migrate/20230405202939_remove_email_confirmation_from_users.rb
================================================
class RemoveEmailConfirmationFromUsers < ActiveRecord::Migration[7.0]
def change
safety_assured { remove_column :users, :email_confirmation, :string }
end
end
================================================
FILE: db/migrate/20230412103356_add_casa_case_to_placements.rb
================================================
class AddCasaCaseToPlacements < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_reference :placements, :casa_case, null: false, index: {algorithm: :concurrently}
end
end
================================================
FILE: db/migrate/20230420212437_add_table_columns_display_to_preference_set.rb
================================================
class AddTableColumnsDisplayToPreferenceSet < ActiveRecord::Migration[7.0]
def change
add_column :preference_sets, :table_state, :jsonb, default: {}
end
end
================================================
FILE: db/migrate/20230610153139_add_receive_reimbursement_email_to_users.rb
================================================
class AddReceiveReimbursementEmailToUsers < ActiveRecord::Migration[7.0]
def change
return if column_exists?(:users, :receive_reimbursement_email)
add_column :users, :receive_reimbursement_email, :boolean, default: false
end
end
================================================
FILE: db/migrate/20230615155223_add_twilio_enabled_to_casa_orgs.rb
================================================
class AddTwilioEnabledToCasaOrgs < ActiveRecord::Migration[7.0]
def change
add_column :casa_orgs, :twilio_enabled, :boolean, default: false
end
end
================================================
FILE: db/migrate/20230621161252_add_additional_expenses_to_casa_orgs.rb
================================================
class AddAdditionalExpensesToCasaOrgs < ActiveRecord::Migration[7.0]
def change
add_column :casa_orgs, :additional_expenses_enabled, :boolean, default: false
end
end
================================================
FILE: db/migrate/20230627210040_add_allow_reimbursement_to_case_assignments.rb
================================================
class AddAllowReimbursementToCaseAssignments < ActiveRecord::Migration[7.0]
def change
add_column :case_assignments, :allow_reimbursement, :boolean, default: true
end
end
================================================
FILE: db/migrate/20230704123327_add_foreign_key_constraints_to_mileage_rates.rb
================================================
class AddForeignKeyConstraintsToMileageRates < ActiveRecord::Migration[7.0]
def change
add_foreign_key :mileage_rates, :casa_orgs, column: :casa_org_id, validate: false
end
end
================================================
FILE: db/migrate/20230710025852_add_token_to_users.rb
================================================
class AddTokenToUsers < ActiveRecord::Migration[7.0]
def up
add_column :users, :token, :string
end
def down
remove_column :users, :token, :string
end
end
================================================
FILE: db/migrate/20230712080040_add_foreign_key_creator_id_to_note.rb
================================================
class AddForeignKeyCreatorIdToNote < ActiveRecord::Migration[7.0]
def up
add_foreign_key :notes, :users, column: :creator_id, validate: false
end
def down
remove_foreign_key :notes, :creator_id
end
end
================================================
FILE: db/migrate/20230728135743_create_action_text_tables.action_text.rb
================================================
# This migration comes from action_text (originally 20180528164100)
class CreateActionTextTables < ActiveRecord::Migration[6.0]
def change
# Use Active Record's configured type for primary and foreign keys
primary_key_type, foreign_key_type = primary_and_foreign_key_types
create_table :action_text_rich_texts, id: primary_key_type do |t|
t.string :name, null: false
t.text :body, size: :long
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
t.timestamps
t.index [:record_type, :record_id, :name], name: "index_action_text_rich_texts_uniqueness", unique: true
end
end
private
def primary_and_foreign_key_types
config = Rails.configuration.generators
setting = config.options[config.orm][:primary_key_type]
primary_key_type = setting || :primary_key
foreign_key_type = setting || :bigint
[primary_key_type, foreign_key_type]
end
end
================================================
FILE: db/migrate/20230728140249_create_banners.rb
================================================
class CreateBanners < ActiveRecord::Migration[7.0]
def change
create_table :banners do |t|
t.references :casa_org, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.string :name
t.boolean :active, default: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230729143126_create_learning_hour_types.rb
================================================
class CreateLearningHourTypes < ActiveRecord::Migration[7.0]
def change
create_table :learning_hour_types do |t|
t.references :casa_org, null: false, foreign_key: true
t.string :name
t.boolean :active, default: true
t.integer :position, default: 1
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230729145310_add_reference_learning_hour_types.rb
================================================
class AddReferenceLearningHourTypes < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_reference :learning_hours, :learning_hour_type, validate: false, index: {algorithm: :concurrently}
end
end
================================================
FILE: db/migrate/20230729145351_add_foreign_key_learning_hour_types.rb
================================================
class AddForeignKeyLearningHourTypes < ActiveRecord::Migration[7.0]
def change
add_foreign_key :learning_hours, :learning_hour_types, validate: false
end
end
================================================
FILE: db/migrate/20230729145419_validate_foreign_key_learning_hour_types.rb
================================================
class ValidateForeignKeyLearningHourTypes < ActiveRecord::Migration[7.0]
def change
validate_foreign_key :learning_hours, :learning_hour_types
end
end
================================================
FILE: db/migrate/20230729154529_create_case_groups.rb
================================================
class CreateCaseGroups < ActiveRecord::Migration[7.0]
def change
create_table :case_groups do |t|
t.references :casa_org, null: false, foreign_key: true
t.string :name
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230729154545_create_case_group_memberships.rb
================================================
class CreateCaseGroupMemberships < ActiveRecord::Migration[7.0]
def change
create_table :case_group_memberships do |t|
t.references :case_group, null: false, foreign_key: true
t.references :casa_case, null: false, foreign_key: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230729213608_remove_hearing_type_id_and_judge_id_from_casa_cases.rb
================================================
class RemoveHearingTypeIdAndJudgeIdFromCasaCases < ActiveRecord::Migration[7.0]
def change
safety_assured { remove_column :casa_cases, :hearing_type_id }
safety_assured { remove_column :casa_cases, :judge_id }
end
end
================================================
FILE: db/migrate/20230730103110_remove_learning_type.rb
================================================
class RemoveLearningType < ActiveRecord::Migration[7.0]
def change
safety_assured { remove_column :learning_hours, :learning_type, :integer, default: 5, not_null: true }
end
end
================================================
FILE: db/migrate/20230809002819_drop_languages_users.rb
================================================
class DropLanguagesUsers < ActiveRecord::Migration[7.0]
def up
drop_table :languages_users
end
def down
fail ActiveRecord::IrreversibleMigration
end
end
================================================
FILE: db/migrate/20230817144910_create_learning_hour_topics.rb
================================================
class CreateLearningHourTopics < ActiveRecord::Migration[7.0]
def change
create_table :learning_hour_topics do |t|
t.string :name, null: false
t.references :casa_org, null: false, foreign_key: true
t.integer :position, default: 1
t.timestamps
end
end
end
================================================
FILE: db/migrate/20230819124840_add_learning_hour_topic_id_to_learning_hour.rb
================================================
class AddLearningHourTopicIdToLearningHour < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def change
add_reference :learning_hours, :learning_hour_topic, index: {algorithm: :concurrently}
end
end
================================================
FILE: db/migrate/20230819132316_add_learning_topic_active_to_casa_org.rb
================================================
class AddLearningTopicActiveToCasaOrg < ActiveRecord::Migration[7.0]
def change
add_column :casa_orgs, :learning_topic_active, :boolean, default: false
end
end
================================================
FILE: db/migrate/20230822152341_drop_jwt_denylist_table.rb
================================================
class DropJwtDenylistTable < ActiveRecord::Migration[7.0]
def change
drop_table :jwt_denylist, if_exists: true
end
end
================================================
FILE: db/migrate/20230902021531_add_monthly_learning_hours_report_to_user.rb
================================================
class AddMonthlyLearningHoursReportToUser < ActiveRecord::Migration[7.0]
def change
add_column :users, :monthly_learning_hours_report, :boolean, default: false, null: false
end
end
================================================
FILE: db/migrate/20230903182657_add_foreign_key_casa_case_to_placement.rb
================================================
class AddForeignKeyCasaCaseToPlacement < ActiveRecord::Migration[7.0]
def change
add_foreign_key :placements, :casa_cases, column: :casa_case_id, validate: false
end
end
================================================
FILE: db/migrate/20231102181027_add_birthdays_to_users.rb
================================================
class AddBirthdaysToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :date_of_birth, :datetime
end
end
================================================
FILE: db/migrate/20231125150721_status_for_case_contacts.rb
================================================
class StatusForCaseContacts < ActiveRecord::Migration[7.0]
def change
add_column :case_contacts, :status, :string, default: "started"
add_column :case_contacts, :draft_case_ids, :integer, array: true, default: []
add_column :case_contacts, :volunteer_address, :string
# We need these columns to be nil if the case_contact is in_progress (ie. DRAFT mode)
change_column_null :case_contacts, :casa_case_id, true
change_column_null :case_contacts, :duration_minutes, true
change_column_null :case_contacts, :occurred_at, true
end
end
================================================
FILE: db/migrate/20240216013254_add_contact_topics.rb
================================================
class AddContactTopics < ActiveRecord::Migration[7.1]
def change
create_table :contact_topics do |t|
t.references :casa_org, null: false, foreign_key: true
t.boolean :active, null: false, default: true
t.boolean :soft_delete, null: false, default: false
t.text :details
t.string :question
t.timestamps
end
create_table :contact_topic_answers do |t|
t.text :value
t.references :case_contact, null: false, foreign_key: true
t.references :contact_topic, null: false, foreign_key: true
t.boolean :selected, null: false, default: false
t.timestamps
end
end
end
================================================
FILE: db/migrate/20240415160842_add_followupable_to_followups.rb
================================================
class AddFollowupableToFollowups < ActiveRecord::Migration[7.1]
disable_ddl_transaction!
# To handle Dangerous operation detected by #strong_migrations in converting followups to polymorphic we will:
# 1. Create a new column
# 2. Write to both columns
# 3. Backfill data from the old column to the new column
# 4. Move reads from the old column to the new column
# 5. Stop writing to the old column
# 6. Drop the old column
#
def change
add_column :followups, :followupable_id, :bigint
add_column :followups, :followupable_type, :string
add_index :followups, [:followupable_type, :followupable_id], algorithm: :concurrently
end
end
================================================
FILE: db/migrate/20240507022441_create_login_activities.rb
================================================
class CreateLoginActivities < ActiveRecord::Migration[7.1]
def change
create_table :login_activities do |t|
t.string :scope
t.string :strategy
t.string :identity, index: true
t.boolean :success
t.string :failure_reason
t.references :user, polymorphic: true
t.string :context
t.string :ip, index: true
t.text :user_agent
t.text :referrer
t.string :city
t.string :region
t.string :country
t.float :latitude
t.float :longitude
t.datetime :created_at
end
end
end
================================================
FILE: db/migrate/20240509104733_create_noticed_tables.noticed.rb
================================================
# This migration comes from noticed (originally 20231215190233)
class CreateNoticedTables < ActiveRecord::Migration[6.1]
def change
primary_key_type, foreign_key_type = primary_and_foreign_key_types
create_table :noticed_events, id: primary_key_type do |t|
t.string :type
t.belongs_to :record, polymorphic: true, type: foreign_key_type
if t.respond_to?(:jsonb)
t.jsonb :params
else
t.json :params
end
t.timestamps
end
create_table :noticed_notifications, id: primary_key_type do |t|
t.string :type
t.belongs_to :event, null: false, type: foreign_key_type
t.belongs_to :recipient, polymorphic: true, null: false, type: foreign_key_type
t.datetime :read_at
t.datetime :seen_at
t.timestamps
end
end
private
def primary_and_foreign_key_types
config = Rails.configuration.generators
setting = config.options[config.orm][:primary_key_type]
primary_key_type = setting || :primary_key
foreign_key_type = setting || :bigint
[primary_key_type, foreign_key_type]
end
end
================================================
FILE: db/migrate/20240509104734_add_notifications_count_to_noticed_event.noticed.rb
================================================
# This migration comes from noticed (originally 20240129184740)
class AddNotificationsCountToNoticedEvent < ActiveRecord::Migration[6.1]
def change
add_column :noticed_events, :notifications_count, :integer
end
end
================================================
FILE: db/migrate/20240531172823_add_expires_at_to_banner.rb
================================================
class AddExpiresAtToBanner < ActiveRecord::Migration[7.1]
def change
add_column :banners, :expires_at, :datetime, null: true
end
end
================================================
FILE: db/migrate/20240610071054_add_other_duties_enabled_to_casa_org.rb
================================================
class AddOtherDutiesEnabledToCasaOrg < ActiveRecord::Migration[7.1]
def change
add_column :casa_orgs, :other_duties_enabled, :boolean, default: true
end
end
================================================
FILE: db/migrate/20240621165358_create_flipper_tables.rb
================================================
class CreateFlipperTables < ActiveRecord::Migration[7.1]
def up
create_table :flipper_features do |t|
t.string :key, null: false
t.timestamps null: false
end
add_index :flipper_features, :key, unique: true
create_table :flipper_gates do |t|
t.string :feature_key, null: false
t.string :key, null: false
t.text :value
t.timestamps null: false
end
add_index :flipper_gates, [:feature_key, :key, :value], unique: true, length: {value: 255}
end
def down
drop_table :flipper_gates
drop_table :flipper_features
end
end
================================================
FILE: db/migrate/20240622020203_drop_feature_flags.rb
================================================
class DropFeatureFlags < ActiveRecord::Migration[7.1]
def up
drop_table :feature_flags
end
def down
create_table :feature_flags do |t|
t.string :name, null: false
t.boolean :enabled, null: false, default: false
t.timestamps
end
add_index :feature_flags, :name, unique: true, algorithm: :concurrently
end
end
================================================
FILE: db/migrate/20240716194609_add_metadata_to_case_contacts.rb
================================================
class AddMetadataToCaseContacts < ActiveRecord::Migration[7.1]
def change
add_column :case_contacts, :metadata, :jsonb, default: {}
end
end
================================================
FILE: db/migrate/20241017050129_remove_contact_topic_answer_contact_topic_id_null_constraint.rb
================================================
class RemoveContactTopicAnswerContactTopicIdNullConstraint < ActiveRecord::Migration[7.2]
def change
change_column_null(:contact_topic_answers, :contact_topic_id, true)
end
end
================================================
FILE: db/migrate/20250207080433_remove_token_from_users.rb
================================================
class RemoveTokenFromUsers < ActiveRecord::Migration[7.2]
def change
safety_assured { remove_column :users, :token, :string }
end
end
================================================
FILE: db/migrate/20250207080511_create_api_credentials.rb
================================================
class CreateApiCredentials < ActiveRecord::Migration[7.2]
def change
create_table :api_credentials do |t|
t.references :user, null: false, foreign_key: true
t.string :api_token
t.string :refresh_token
t.datetime :token_expires_at, default: -> { "NOW() + INTERVAL '7 hours'" }
t.datetime :refresh_token_expires_at, default: -> { "NOW() + INTERVAL '30 days'" }
t.string :api_token_digest
t.string :refresh_token_digest
t.timestamps
end
add_index :api_credentials, :api_token_digest, unique: true, where: "api_token_digest IS NOT NULL"
add_index :api_credentials, :refresh_token_digest, unique: true, where: "refresh_token_digest IS NOT NULL"
end
end
================================================
FILE: db/migrate/20250208160513_remove_plain_text_tokens_from_api_credentials.rb
================================================
class RemovePlainTextTokensFromApiCredentials < ActiveRecord::Migration[7.2]
def change
safety_assured { remove_column :api_credentials, :api_token, :string }
safety_assured { remove_column :api_credentials, :refresh_token, :string }
end
end
================================================
FILE: db/migrate/20250331032424_validate_constraint_mileage_rates.rb
================================================
class ValidateConstraintMileageRates < ActiveRecord::Migration[7.2]
def up
ActiveRecord::Base.connection.execute(Arel.sql("ALTER TABLE mileage_rates VALIDATE CONSTRAINT fk_rails_3dad81992f;"))
end
def down
# cannot un-validate a constraint
end
end
================================================
FILE: db/migrate/20250331033339_validate_constraint_notes.rb
================================================
class ValidateConstraintNotes < ActiveRecord::Migration[7.2]
def up
ActiveRecord::Base.connection.execute(Arel.sql("ALTER TABLE notes VALIDATE CONSTRAINT fk_rails_5d4a723a34;"))
end
def down
# cannot un-validate a constraint
end
end
================================================
FILE: db/migrate/20250331033350_validate_constraint_placements.rb
================================================
class ValidateConstraintPlacements < ActiveRecord::Migration[7.2]
def up
ActiveRecord::Base.connection.execute(Arel.sql("ALTER TABLE placements VALIDATE CONSTRAINT fk_rails_65aeeb5669;"))
end
def down
# cannot un-validate a constraint
end
end
================================================
FILE: db/migrate/20250331033418_remove_duplicate_indexindex_emancipation_options_on_emancipation_category_id.rb
================================================
class RemoveDuplicateIndexindexEmancipationOptionsOnEmancipationCategoryId < ActiveRecord::Migration[7.2]
def change
remove_index :emancipation_options, :emancipation_category_id
end
end
================================================
FILE: db/migrate/20250331033441_remove_duplicate_indexindex_user_languages_on_language_id.rb
================================================
class RemoveDuplicateIndexindexUserLanguagesOnLanguageId < ActiveRecord::Migration[7.2]
def change
remove_index :user_languages, :language_id
end
end
================================================
FILE: db/migrate/20250404200715_create_custom_org_links.rb
================================================
class CreateCustomOrgLinks < ActiveRecord::Migration[7.2]
def change
create_table :custom_org_links do |t|
t.references :casa_org, null: false, foreign_key: true
t.string :text, null: false
t.string :url, null: false
t.boolean :active, null: false, default: true
t.timestamps
end
end
end
================================================
FILE: db/migrate/20250507011754_remove_unused_indexes.rb
================================================
class RemoveUnusedIndexes < ActiveRecord::Migration[7.2]
def change
remove_index :case_contacts, name: "index_case_contacts_on_deleted_at", if_exists: true
remove_index :noticed_notifications, name: "index_noticed_notifications_on_event_id", if_exists: true
remove_index :contact_topic_answers, name: "index_contact_topic_answers_on_contact_topic_id", if_exists: true
remove_index :login_activities, name: "index_login_activities_on_ip", if_exists: true
remove_index :login_activities, name: "index_login_activities_on_identity", if_exists: true
remove_index :delayed_jobs, name: "delayed_jobs_priority", if_exists: true
remove_index :login_activities, name: "index_login_activities_on_user", if_exists: true
remove_index :sent_emails, name: "index_sent_emails_on_user_id", if_exists: true
remove_index :sent_emails, name: "index_sent_emails_on_casa_org_id", if_exists: true
remove_index :noticed_events, name: "index_noticed_events_on_record", if_exists: true
remove_index :notifications, name: "index_notifications_on_read_at", if_exists: true
remove_index :notifications, name: "index_notifications_on_recipient", if_exists: true
remove_index :api_credentials, name: "index_api_credentials_on_user_id", if_exists: true
remove_index :court_dates, name: "index_court_dates_on_hearing_type_id", if_exists: true
remove_index :court_dates, name: "index_court_dates_on_judge_id", if_exists: true
remove_index :followups, name: "index_followups_on_followupable_type_and_followupable_id", if_exists: true
remove_index :banners, name: "index_banners_on_casa_org_id", if_exists: true
remove_index :banners, name: "index_banners_on_user_id", if_exists: true
remove_index :casa_case_emancipation_categories, name: "index_case_emancipation_categories_on_emancipation_category_id", if_exists: true
remove_index :case_group_memberships, name: "index_case_group_memberships_on_casa_case_id", if_exists: true
remove_index :case_group_memberships, name: "index_case_group_memberships_on_case_group_id", if_exists: true
remove_index :case_groups, name: "index_case_groups_on_casa_org_id", if_exists: true
remove_index :contact_topics, name: "index_contact_topics_on_casa_org_id", if_exists: true
remove_index :contact_type_groups, name: "index_contact_type_groups_on_casa_org_id", if_exists: true
remove_index :followups, name: "index_followups_on_creator_id", if_exists: true
remove_index :hearing_types, name: "index_hearing_types_on_casa_org_id", if_exists: true
remove_index :judges, name: "index_judges_on_casa_org_id", if_exists: true
remove_index :languages, name: "index_languages_on_casa_org_id", if_exists: true
remove_index :learning_hour_topics, name: "index_learning_hour_topics_on_casa_org_id", if_exists: true
remove_index :learning_hour_types, name: "index_learning_hour_types_on_casa_org_id", if_exists: true
remove_index :learning_hours, name: "index_learning_hours_on_learning_hour_topic_id", if_exists: true
remove_index :learning_hours, name: "index_learning_hours_on_learning_hour_type_id", if_exists: true
remove_index :mileage_rates, name: "index_mileage_rates_on_casa_org_id", if_exists: true
remove_index :mileage_rates, name: "index_mileage_rates_on_user_id", if_exists: true
remove_index :notes, name: "index_notes_on_notable", if_exists: true
remove_index :patch_notes, name: "index_patch_notes_on_patch_note_group_id", if_exists: true
remove_index :patch_notes, name: "index_patch_notes_on_patch_note_type_id", if_exists: true
remove_index :user_languages, name: "index_user_languages_on_user_id", if_exists: true
remove_index :user_sms_notification_events, name: "index_user_sms_notification_events_on_sms_notification_event_id", if_exists: true
remove_index :user_sms_notification_events, name: "index_user_sms_notification_events_on_user_id", if_exists: true
remove_index :users, name: "index_users_on_invitations_count", if_exists: true
remove_index :users, name: "index_users_on_invited_by_type_and_invited_by_id", if_exists: true
remove_index :checklist_items, name: "index_checklist_items_on_hearing_type_id", if_exists: true
remove_index :placement_types, name: "index_placement_types_on_casa_org_id", if_exists: true
remove_index :placements, name: "index_placements_on_casa_case_id", if_exists: true
remove_index :placements, name: "index_placements_on_creator_id", if_exists: true
remove_index :placements, name: "index_placements_on_placement_type_id", if_exists: true
remove_index :user_case_contact_types_reminders, name: "index_user_case_contact_types_reminders_on_user_id", if_exists: true
end
end
================================================
FILE: db/migrate/20250528092341_trim_whitespace_from_custom_org_links.rb
================================================
class TrimWhitespaceFromCustomOrgLinks < ActiveRecord::Migration[7.2]
def up
CustomOrgLink.find_each do |link|
trimmed_text = link.text.strip
link.update_columns(text: trimmed_text) if trimmed_text.present?
rescue => e
Rails.logger.error("Failed to update CustomOrgLink ##{link.id}: #{e.message}")
end
end
def down
Rails.logger.info("Rollback not implemented for TrimWhitespaceFromCustomOrgLinks as it is a data migration")
end
end
================================================
FILE: db/migrate/20250702142004_add_exclude_from_court_report_to_contact_topics.rb
================================================
class AddExcludeFromCourtReportToContactTopics < ActiveRecord::Migration[7.2]
def change
add_column :contact_topics, :exclude_from_court_report, :boolean, default: false, null: false
end
end
================================================
FILE: db/migrate/20260210233737_rename_casa_cases_emancipaton_options_to_casa_case_emancipaton_options.rb
================================================
class RenameCasaCasesEmancipatonOptionsToCasaCaseEmancipatonOptions < ActiveRecord::Migration[7.2]
def change
reversible do |dir|
dir.up do
create_table "casa_case_emancipation_options" do |t|
t.bigint "casa_case_id", null: false
t.bigint "emancipation_option_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["casa_case_id", "emancipation_option_id"], name: "index_case_options_on_case_id_and_option_id", unique: true
end
add_foreign_key "casa_case_emancipation_options", "casa_cases", validate: false
add_foreign_key "casa_case_emancipation_options", "emancipation_options", validate: false
safety_assured do
execute <<-SQL
INSERT INTO casa_case_emancipation_options (casa_case_id, emancipation_option_id, created_at, updated_at) SELECT casa_case_id, emancipation_option_id, created_at, updated_at FROM casa_cases_emancipation_options;
SQL
end
end
dir.down do
remove_foreign_key "casa_case_emancipation_options", "casa_cases", validate: false
remove_foreign_key "casa_case_emancipation_options", "emancipation_options", validate: false
drop_table :casa_case_emancipation_options
end
end
end
end
================================================
FILE: db/migrate/20260211001655_rename_casa_cases_emancipaton_options_to_casa_case_emancipaton_options_follow_up.rb
================================================
class RenameCasaCasesEmancipatonOptionsToCasaCaseEmancipatonOptionsFollowUp < ActiveRecord::Migration[7.2]
def up
validate_foreign_key "casa_case_emancipation_options", "casa_cases"
validate_foreign_key "casa_case_emancipation_options", "emancipation_options"
drop_table :casa_cases_emancipation_options
end
def down
fail ActiveRecord::IrreversibleMigration
end
end
================================================
FILE: db/migrate/20260414132818_add_deleted_at_to_contact_topic_answers.rb
================================================
class AddDeletedAtToContactTopicAnswers < ActiveRecord::Migration[7.2]
def change
add_column :contact_topic_answers, :deleted_at, :datetime
end
end
================================================
FILE: db/schema.rb
================================================
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2026_04_14_132818) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "action_text_rich_texts", force: :cascade do |t|
t.string "name", null: false
t.text "body"
t.string "record_type", null: false
t.bigint "record_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
end
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.bigint "record_id", null: false
t.bigint "blob_id", null: false
t.datetime "created_at", precision: nil, null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.bigint "byte_size", null: false
t.string "checksum"
t.datetime "created_at", precision: nil, null: false
t.string "service_name", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", force: :cascade do |t|
t.bigint "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "additional_expenses", force: :cascade do |t|
t.bigint "case_contact_id", null: false
t.decimal "other_expense_amount"
t.string "other_expenses_describe"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["case_contact_id"], name: "index_additional_expenses_on_case_contact_id"
end
create_table "addresses", force: :cascade do |t|
t.string "content"
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_addresses_on_user_id"
end
create_table "all_casa_admins", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "invitation_token"
t.datetime "invitation_created_at", precision: nil
t.datetime "invitation_sent_at", precision: nil
t.datetime "invitation_accepted_at", precision: nil
t.integer "invitation_limit"
t.integer "invited_by_id"
t.string "invited_by_type"
t.index ["email"], name: "index_all_casa_admins_on_email", unique: true
t.index ["invitation_token"], name: "index_all_casa_admins_on_invitation_token", unique: true
t.index ["reset_password_token"], name: "index_all_casa_admins_on_reset_password_token", unique: true
end
create_table "api_credentials", force: :cascade do |t|
t.bigint "user_id", null: false
t.datetime "token_expires_at", default: -> { "(now() + 'PT7H'::interval)" }
t.datetime "refresh_token_expires_at", default: -> { "(now() + 'P30D'::interval)" }
t.string "api_token_digest"
t.string "refresh_token_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["api_token_digest"], name: "index_api_credentials_on_api_token_digest", unique: true, where: "(api_token_digest IS NOT NULL)"
t.index ["refresh_token_digest"], name: "index_api_credentials_on_refresh_token_digest", unique: true, where: "(refresh_token_digest IS NOT NULL)"
end
create_table "banners", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.bigint "user_id", null: false
t.string "name"
t.boolean "active", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "expires_at"
end
create_table "casa_case_contact_types", force: :cascade do |t|
t.bigint "contact_type_id", null: false
t.bigint "casa_case_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["casa_case_id"], name: "index_casa_case_contact_types_on_casa_case_id"
t.index ["contact_type_id"], name: "index_casa_case_contact_types_on_contact_type_id"
end
create_table "casa_case_emancipation_categories", force: :cascade do |t|
t.bigint "casa_case_id", null: false
t.bigint "emancipation_category_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["casa_case_id"], name: "index_casa_case_emancipation_categories_on_casa_case_id"
end
create_table "casa_case_emancipation_options", force: :cascade do |t|
t.bigint "casa_case_id", null: false
t.bigint "emancipation_option_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["casa_case_id", "emancipation_option_id"], name: "index_case_options_on_case_id_and_option_id", unique: true
end
create_table "casa_cases", force: :cascade do |t|
t.string "case_number", null: false
t.boolean "transition_aged_youth", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "casa_org_id", null: false
t.datetime "birth_month_year_youth", precision: nil
t.datetime "court_report_due_date", precision: nil
t.boolean "active", default: true, null: false
t.datetime "court_report_submitted_at", precision: nil
t.integer "court_report_status", default: 0
t.string "slug"
t.datetime "date_in_care"
t.index ["casa_org_id"], name: "index_casa_cases_on_casa_org_id"
t.index ["case_number", "casa_org_id"], name: "index_casa_cases_on_case_number_and_casa_org_id", unique: true
t.index ["slug"], name: "index_casa_cases_on_slug"
end
create_table "casa_orgs", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "display_name"
t.string "address"
t.string "footer_links", default: [], array: true
t.string "slug"
t.boolean "show_driving_reimbursement", default: true
t.boolean "show_fund_request", default: false
t.string "twilio_phone_number"
t.string "twilio_account_sid"
t.string "twilio_api_key_sid"
t.string "twilio_api_key_secret"
t.boolean "twilio_enabled", default: false
t.boolean "additional_expenses_enabled", default: false
t.boolean "learning_topic_active", default: false
t.boolean "other_duties_enabled", default: true
t.index ["slug"], name: "index_casa_orgs_on_slug", unique: true
end
create_table "case_assignments", force: :cascade do |t|
t.bigint "casa_case_id", null: false
t.bigint "volunteer_id", null: false
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "hide_old_contacts", default: false
t.boolean "allow_reimbursement", default: true
t.index ["casa_case_id"], name: "index_case_assignments_on_casa_case_id"
t.index ["volunteer_id"], name: "index_case_assignments_on_volunteer_id"
end
create_table "case_contact_contact_types", force: :cascade do |t|
t.bigint "case_contact_id", null: false
t.bigint "contact_type_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["case_contact_id"], name: "index_case_contact_contact_types_on_case_contact_id"
t.index ["contact_type_id"], name: "index_case_contact_contact_types_on_contact_type_id"
end
create_table "case_contacts", force: :cascade do |t|
t.bigint "creator_id", null: false
t.bigint "casa_case_id"
t.integer "duration_minutes"
t.datetime "occurred_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "contact_made", default: false
t.string "medium_type"
t.integer "miles_driven", default: 0, null: false
t.boolean "want_driving_reimbursement", default: false
t.string "notes"
t.datetime "deleted_at", precision: nil
t.boolean "reimbursement_complete", default: false
t.string "status", default: "started"
t.integer "draft_case_ids", default: [], array: true
t.string "volunteer_address"
t.jsonb "metadata", default: {}
t.index ["casa_case_id"], name: "index_case_contacts_on_casa_case_id"
t.index ["creator_id"], name: "index_case_contacts_on_creator_id"
t.check_constraint "miles_driven IS NOT NULL OR NOT want_driving_reimbursement", name: "want_driving_reimbursement_only_when_miles_driven"
end
create_table "case_court_orders", force: :cascade do |t|
t.bigint "casa_case_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "implementation_status"
t.bigint "court_date_id"
t.string "text"
t.index ["casa_case_id"], name: "index_case_court_orders_on_casa_case_id"
t.index ["court_date_id"], name: "index_case_court_orders_on_court_date_id"
end
create_table "case_group_memberships", force: :cascade do |t|
t.bigint "case_group_id", null: false
t.bigint "casa_case_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "case_groups", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "checklist_items", force: :cascade do |t|
t.integer "hearing_type_id"
t.text "description", null: false
t.string "category", null: false
t.boolean "mandatory", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "contact_topic_answers", force: :cascade do |t|
t.text "value"
t.bigint "case_contact_id", null: false
t.bigint "contact_topic_id"
t.boolean "selected", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "deleted_at"
t.index ["case_contact_id"], name: "index_contact_topic_answers_on_case_contact_id"
end
create_table "contact_topics", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.boolean "active", default: true, null: false
t.boolean "soft_delete", default: false, null: false
t.text "details"
t.string "question"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "exclude_from_court_report", default: false, null: false
end
create_table "contact_type_groups", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "active", default: true
end
create_table "contact_types", force: :cascade do |t|
t.bigint "contact_type_group_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "active", default: true
t.index ["contact_type_group_id"], name: "index_contact_types_on_contact_type_group_id"
end
create_table "court_dates", force: :cascade do |t|
t.datetime "date", precision: nil, null: false
t.bigint "casa_case_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "hearing_type_id"
t.bigint "judge_id"
t.datetime "court_report_due_date", precision: nil
t.index ["casa_case_id"], name: "index_court_dates_on_casa_case_id"
end
create_table "custom_org_links", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.string "text", null: false
t.string "url", null: false
t.boolean "active", default: true, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["casa_org_id"], name: "index_custom_org_links_on_casa_org_id"
end
create_table "delayed_jobs", force: :cascade do |t|
t.integer "priority", default: 0, null: false
t.integer "attempts", default: 0, null: false
t.text "handler", null: false
t.text "last_error"
t.datetime "run_at", precision: nil
t.datetime "locked_at", precision: nil
t.datetime "failed_at", precision: nil
t.string "locked_by"
t.string "queue"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "emancipation_categories", force: :cascade do |t|
t.string "name", null: false
t.boolean "mutually_exclusive", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name"], name: "index_emancipation_categories_on_name", unique: true
end
create_table "emancipation_options", force: :cascade do |t|
t.bigint "emancipation_category_id", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["emancipation_category_id", "name"], name: "index_emancipation_options_on_emancipation_category_id_and_name", unique: true
end
create_table "flipper_features", force: :cascade do |t|
t.string "key", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["key"], name: "index_flipper_features_on_key", unique: true
end
create_table "flipper_gates", force: :cascade do |t|
t.string "feature_key", null: false
t.string "key", null: false
t.text "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true
end
create_table "followups", force: :cascade do |t|
t.bigint "case_contact_id"
t.bigint "creator_id"
t.integer "status", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "note"
t.bigint "followupable_id"
t.string "followupable_type"
t.index ["case_contact_id"], name: "index_followups_on_case_contact_id"
end
create_table "fund_requests", force: :cascade do |t|
t.text "submitter_email"
t.text "youth_name"
t.text "payment_amount"
t.text "deadline"
t.text "request_purpose"
t.text "payee_name"
t.text "requested_by_and_relationship"
t.text "other_funding_source_sought"
t.text "impact"
t.text "extra_information"
t.text "timestamps"
end
create_table "healths", force: :cascade do |t|
t.datetime "latest_deploy_time", precision: nil
t.integer "singleton_guard"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["singleton_guard"], name: "index_healths_on_singleton_guard", unique: true
end
create_table "hearing_types", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.string "name", null: false
t.boolean "active", default: true, null: false
t.string "checklist_updated_date", default: "None", null: false
end
create_table "judges", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "active", default: true
t.string "name"
end
create_table "languages", force: :cascade do |t|
t.string "name"
t.bigint "casa_org_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "learning_hour_topics", force: :cascade do |t|
t.string "name", null: false
t.bigint "casa_org_id", null: false
t.integer "position", default: 1
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "learning_hour_types", force: :cascade do |t|
t.bigint "casa_org_id", null: false
t.string "name"
t.boolean "active", default: true
t.integer "position", default: 1
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "learning_hours", force: :cascade do |t|
t.bigint "user_id", null: false
t.string "name", null: false
t.integer "duration_minutes", null: false
t.integer "duration_hours", null: false
t.datetime "occurred_at", precision: nil, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "learning_hour_type_id"
t.bigint "learning_hour_topic_id"
t.index ["user_id"], name: "index_learning_hours_on_user_id"
end
create_table "login_activities", force: :cascade do |t|
t.string "scope"
t.string "strategy"
t.string "identity"
t.boolean "success"
t.string "failure_reason"
t.string "user_type"
t.bigint "user_id"
t.string "context"
t.string "ip"
t.text "user_agent"
t.text "referrer"
t.string "city"
t.string "region"
t.string "country"
t.float "latitude"
t.float "longitude"
t.datetime "created_at"
end
create_table "mileage_rates", force: :cascade do |t|
t.decimal "amount"
t.date "effective_date"
t.boolean "is_active", default: true
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "casa_org_id", null: false
end
create_table "notes", force: :cascade do |t|
t.string "content"
t.bigint "creator_id"
t.string "notable_type"
t.bigint "notable_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "noticed_events", force: :cascade do |t|
t.string "type"
t.string "record_type"
t.bigint "record_id"
t.jsonb "params"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "notifications_count"
end
create_table "noticed_notifications", force: :cascade do |t|
t.string "type"
t.bigint "event_id", null: false
t.string "recipient_type", null: false
t.bigint "recipient_id", null: false
t.datetime "read_at", precision: nil
t.datetime "seen_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["recipient_type", "recipient_id"], name: "index_noticed_notifications_on_recipient"
end
create_table "notifications", force: :cascade do |t|
t.string "recipient_type", null: false
t.bigint "recipient_id", null: false
t.string "type", null: false
t.jsonb "params"
t.datetime "read_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "other_duties", force: :cascade do |t|
t.bigint "creator_id", null: false
t.string "creator_type"
t.datetime "occurred_at", precision: nil
t.bigint "duration_minutes"
t.text "notes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "patch_note_groups", force: :cascade do |t|
t.string "value", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["value"], name: "index_patch_note_groups_on_value", unique: true
end
create_table "patch_note_types", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name"], name: "index_patch_note_types_on_name", unique: true
end
create_table "patch_notes", force: :cascade do |t|
t.text "note", null: false
t.bigint "patch_note_type_id", null: false
t.bigint "patch_note_group_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "placement_types", force: :cascade do |t|
t.string "name", null: false
t.bigint "casa_org_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "placements", force: :cascade do |t|
t.datetime "placement_started_at", null: false
t.bigint "placement_type_id", null: false
t.bigint "creator_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "casa_case_id", null: false
end
create_table "preference_sets", force: :cascade do |t|
t.bigint "user_id"
t.jsonb "case_volunteer_columns", default: "{}", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "table_state", default: {}
t.index ["user_id"], name: "index_preference_sets_on_user_id"
end
create_table "sent_emails", force: :cascade do |t|
t.bigint "user_id"
t.bigint "casa_org_id", null: false
t.string "mailer_type"
t.string "category"
t.string "sent_address"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sms_notification_events", force: :cascade do |t|
t.string "name"
t.string "user_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "supervisor_volunteers", force: :cascade do |t|
t.bigint "supervisor_id", null: false
t.bigint "volunteer_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "is_active", default: true
t.index ["supervisor_id"], name: "index_supervisor_volunteers_on_supervisor_id"
t.index ["volunteer_id"], name: "index_supervisor_volunteers_on_volunteer_id"
end
create_table "task_records", id: false, force: :cascade do |t|
t.string "version", null: false
end
create_table "user_languages", force: :cascade do |t|
t.bigint "user_id"
t.bigint "language_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["language_id", "user_id"], name: "index_user_languages_on_language_id_and_user_id", unique: true
end
create_table "user_reminder_times", force: :cascade do |t|
t.bigint "user_id", null: false
t.datetime "case_contact_types"
t.datetime "no_contact_made"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_user_reminder_times_on_user_id"
end
create_table "user_sms_notification_events", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "sms_notification_event_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "casa_org_id", null: false
t.string "display_name", default: "", null: false
t.string "invitation_token"
t.datetime "invitation_created_at", precision: nil
t.datetime "invitation_sent_at", precision: nil
t.datetime "invitation_accepted_at", precision: nil
t.integer "invitation_limit"
t.string "invited_by_type"
t.bigint "invited_by_id"
t.integer "invitations_count", default: 0
t.string "type"
t.boolean "active", default: true
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at", precision: nil
t.datetime "last_sign_in_at", precision: nil
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "phone_number", default: ""
t.boolean "receive_sms_notifications", default: false, null: false
t.boolean "receive_email_notifications", default: true
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.string "old_emails", default: [], array: true
t.boolean "receive_reimbursement_email", default: false
t.boolean "monthly_learning_hours_report", default: false, null: false
t.datetime "date_of_birth"
t.index ["casa_org_id"], name: "index_users_on_casa_org_id"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "additional_expenses", "case_contacts"
add_foreign_key "addresses", "users"
add_foreign_key "api_credentials", "users"
add_foreign_key "banners", "casa_orgs"
add_foreign_key "banners", "users"
add_foreign_key "casa_case_emancipation_categories", "casa_cases"
add_foreign_key "casa_case_emancipation_categories", "emancipation_categories"
add_foreign_key "casa_case_emancipation_options", "casa_cases"
add_foreign_key "casa_case_emancipation_options", "emancipation_options"
add_foreign_key "casa_cases", "casa_orgs"
add_foreign_key "case_assignments", "casa_cases"
add_foreign_key "case_assignments", "users", column: "volunteer_id"
add_foreign_key "case_contacts", "casa_cases"
add_foreign_key "case_contacts", "users", column: "creator_id"
add_foreign_key "case_court_orders", "casa_cases"
add_foreign_key "case_group_memberships", "casa_cases"
add_foreign_key "case_group_memberships", "case_groups"
add_foreign_key "case_groups", "casa_orgs"
add_foreign_key "contact_topic_answers", "case_contacts"
add_foreign_key "contact_topic_answers", "contact_topics"
add_foreign_key "contact_topics", "casa_orgs"
add_foreign_key "court_dates", "casa_cases"
add_foreign_key "custom_org_links", "casa_orgs"
add_foreign_key "emancipation_options", "emancipation_categories"
add_foreign_key "followups", "users", column: "creator_id"
add_foreign_key "judges", "casa_orgs"
add_foreign_key "languages", "casa_orgs"
add_foreign_key "learning_hour_topics", "casa_orgs"
add_foreign_key "learning_hour_types", "casa_orgs"
add_foreign_key "learning_hours", "learning_hour_types"
add_foreign_key "learning_hours", "users"
add_foreign_key "mileage_rates", "casa_orgs"
add_foreign_key "mileage_rates", "users"
add_foreign_key "notes", "users", column: "creator_id"
add_foreign_key "other_duties", "users", column: "creator_id"
add_foreign_key "patch_notes", "patch_note_groups"
add_foreign_key "patch_notes", "patch_note_types"
add_foreign_key "placement_types", "casa_orgs"
add_foreign_key "placements", "casa_cases"
add_foreign_key "placements", "placement_types"
add_foreign_key "placements", "users", column: "creator_id"
add_foreign_key "preference_sets", "users"
add_foreign_key "sent_emails", "casa_orgs"
add_foreign_key "sent_emails", "users"
add_foreign_key "supervisor_volunteers", "users", column: "supervisor_id"
add_foreign_key "supervisor_volunteers", "users", column: "volunteer_id"
add_foreign_key "user_reminder_times", "users"
add_foreign_key "user_sms_notification_events", "sms_notification_events"
add_foreign_key "user_sms_notification_events", "users"
add_foreign_key "users", "casa_orgs"
end
================================================
FILE: db/seeds/api_credential_data.rb
================================================
ApiCredential.destroy_all
users = User.all
users.each do |user|
ApiCredential.create!(user: user, api_token_digest: Digest::SHA256.hexdigest(SecureRandom.hex(18)), refresh_token_digest: Digest::SHA256.hexdigest(SecureRandom.hex(18)))
end
================================================
FILE: db/seeds/casa_org_populator_presets.rb
================================================
# Preset option sets for various sizes and for the Rails environments.
# These do not include `org_name`, which can be provided separately by the caller to override default name.
module CasaOrgPopulatorPresets
module_function
def minimal_dataset_options
{
case_count: 1,
volunteer_count: 1,
supervisor_count: 1,
casa_admin_count: 1
}
end
def small_dataset_options
{
case_count: 8,
volunteer_count: 5,
supervisor_count: 2,
casa_admin_count: 1
}
end
def medium_dataset_options
{
case_count: 75,
volunteer_count: 50,
supervisor_count: 4,
casa_admin_count: 2
}
end
def large_dataset_options
{
case_count: 160,
volunteer_count: 100,
supervisor_count: 10,
casa_admin_count: 3
}
end
def for_environment
{
"development" => CasaOrgPopulatorPresets.small_dataset_options,
"qa" => CasaOrgPopulatorPresets.large_dataset_options,
"staging" => CasaOrgPopulatorPresets.large_dataset_options,
"test" => CasaOrgPopulatorPresets.small_dataset_options
}[ENV["APP_ENVIRONMENT"] || Rails.env]
end
end
================================================
FILE: db/seeds/db_populator.rb
================================================
# Called by the seeding process to create data with a specified random number generator.
# There is a 1 in 30 probability that a Volunteer will be inactive when created.
# There is no instance of a volunteer who was previously assigned a case being inactivated.
# Email addresses generated will be globally unique across all orgs.
class DbPopulator
SEED_PASSWORD = "12345678"
WORD_LENGTH_TUNING = 10
LINE_BREAK_TUNING = 5
PREFIX_OPTIONS = ("A".ord.."Z".ord).to_a.map(&:chr)
attr_reader :rng
# Public Methods
# Pass an instance of Random for Faker and Ruby `rand` and sample` calls.
def initialize(random_instance, case_fourteen_years_old: false)
@rng = random_instance
@casa_org_counter = 0
@case_number_sequence = 1000
@case_fourteen_years_old = case_fourteen_years_old
end
def create_all_casa_admin(email)
return if AllCasaAdmin.find_by(email: email)
AllCasaAdmin.create!(email: email, password: SEED_PASSWORD, password_confirmation: SEED_PASSWORD)
end
def create_org(options)
@casa_org_counter += 1
options.org_name ||= "CASA Organization ##{@casa_org_counter}"
CasaOrg.find_or_create_by!(name: options.org_name) { |org|
org.name = options.org_name
org.display_name = options.org_name
org.address = Faker::Address.full_address
org.footer_links = [
["https://example.org/contact/", "Contact Us"],
["https://example.org/subscribe-to-newsletter/", "Subscribe to newsletter"],
["https://www.example.org/give/givefrm.asp?CID=4450", "Donate"]
]
org.logo.attach(io: File.open(CasaOrg::CASA_DEFAULT_LOGO), filename: CasaOrg::CASA_DEFAULT_LOGO.basename.to_s)
}
end
# Create 2 judges for each casa_org.
def create_judges(casa_org)
2.times { Judge.create(name: Faker::Name.name, casa_org: casa_org) }
end
# Creates 3 users, 1 each for [Volunteer, Supervisor, CasaAdmin].
# For org's after the first one created, adds an org number to the email address so that they will be globally unique
def create_users(casa_org, options)
# Generate email address; for orgs only after first org, and org number would be added, e.g.:
# Org #1: volunteer1@example.com
# Org #2: volunteer2-1@example.com
email = ->(klass, n) do
org_fragment = (@casa_org_counter > 1) ? "#{@casa_org_counter}-" : ""
klass.name.underscore + org_fragment + n.to_s + "@example.com"
end
create_users_of_type = ->(klass, count) do
(1..count).each do |n|
current_email = email.call(klass, n)
attributes = {
casa_org: casa_org,
email: current_email,
password: SEED_PASSWORD,
password_confirmation: SEED_PASSWORD,
display_name: Faker::Name.name,
phone_number: Faker::PhoneNumber.cell_phone_in_e164,
active: true,
confirmed_at: Time.now
}
# Approximately 1 out of 30 volunteers should be set to inactive.
if klass == Volunteer && rng.rand(30) == 0
attributes[:active] = false
end
unless klass.find_by(email: current_email)
klass.create!(attributes)
end
end
end
create_users_of_type.call(CasaAdmin, options.casa_admin_count)
create_users_of_type.call(Supervisor, options.supervisor_count)
create_users_of_type.call(Volunteer, options.volunteer_count)
supervisors = Supervisor.all.to_a
Volunteer.all.each { |v| v.supervisor = supervisors.sample(random: rng) }
end
# Create other duties (Volunteer only)
# Increment other_duties_counter by 1 each time other duty is created
# Print out statement that indicates number of other duties created
def create_other_duties
Volunteer.find_each do |v|
2.times {
OtherDuty.create!(
creator_id: v.id,
creator_type: "Volunteer",
occurred_at: Faker::Date.between(from: 2.days.ago, to: Date.today),
duration_minutes: rand(5..180),
notes: Faker::Lorem.sentence
)
}
end
end
def create_case_contacts(casa_case)
draft_statuses = %w[started details notes expenses]
case_contact_statuses = Array.new(random_zero_one_count, draft_statuses.sample(random: rng)) +
Array.new(random_case_contact_count, "active")
case_contact_statuses.shuffle(random: rng).each do |status|
create_case_contact(casa_case, status:)
end
end
def create_case_contact(casa_case, status: "active")
params = base_case_contact_params(casa_case, status)
status_modifications = {
"started" => {want_driving_reimbursement: false, draft_case_ids: [], medium_type: nil, occurred_at: nil, duration_minutes: nil, notes: nil, miles_driven: 0},
"details" => {want_driving_reimbursement: false, notes: nil, miles_driven: 0},
"notes" => {want_driving_reimbursement: false, miles_driven: 0},
"expenses" => {want_driving_reimbursement: true}
}
params.merge!(status_modifications[status]) unless status == "active"
CaseContact.create!(params)
end
def create_cases(casa_org, options)
ContactTypePopulator.populate
options.case_count.times do |index|
case_number = generate_case_number
court_date = generate_court_date
court_report_submitted = index.even?
new_casa_case = CasaCase.find_by(case_number: case_number)
birth_month_year_youth = @case_fourteen_years_old ? ((Date.today - 18.year)..(Date.today - CasaCase::TRANSITION_AGE.year)).to_a.sample : ((Date.today - 18.year)..(Date.today - 1.year)).to_a.sample
new_casa_case ||= CasaCase.find_or_create_by!(
casa_org_id: casa_org.id,
case_number: case_number,
court_report_submitted_at: court_report_submitted ? Date.today : nil,
court_report_status: court_report_submitted ? :submitted : :not_submitted,
birth_month_year_youth: birth_month_year_youth,
date_in_care: Date.today - (rand * 1500)
)
new_court_date = CourtDate.find_or_create_by!(
casa_case: new_casa_case,
court_report_due_date: court_date + 1.month,
date: court_date
)
volunteer = new_casa_case.casa_org.volunteers.active.sample(random: rng) ||
new_casa_case.casa_org.volunteers.active.first ||
Volunteer.create!(
casa_org: new_casa_case.casa_org,
email: "#{SecureRandom.hex(10)}@example.com",
password: SEED_PASSWORD,
display_name: "active volunteer"
)
CaseAssignment.find_or_create_by!(casa_case: new_casa_case, volunteer: volunteer)
random_court_order_count.times do
CaseCourtOrder.create!(
casa_case_id: new_casa_case.id,
court_date: new_court_date,
text: order_choices.sample(random: rng),
implementation_status: CaseCourtOrder::IMPLEMENTATION_STATUSES.values.sample(random: rng)
)
end
random_zero_one_count.times do |index|
CourtDate.create!(
casa_case_id: new_casa_case.id,
date: Date.today + 5.weeks
)
end
random_past_court_date_count.times do |index|
CourtDate.create!(
casa_case_id: new_casa_case.id,
date: Date.today - (index + 1).weeks
)
end
create_case_contacts(new_casa_case)
# guarantee at least one case contact before and after most recent past court date
if most_recent_past_court_date(new_casa_case.id)
if !case_contact_before_last_court_date?(new_casa_case.id, most_recent_past_court_date(new_casa_case.id))
new_case_contact = create_case_contact(new_casa_case)
new_case_contact.occurred_at = most_recent_past_court_date(new_casa_case.id) - 24.hours
new_case_contact.save!
end
if !case_contact_after_last_court_date?(new_casa_case.id, most_recent_past_court_date(new_casa_case.id))
new_case_contact = create_case_contact(new_casa_case)
new_case_contact.occurred_at = most_recent_past_court_date(new_casa_case.id) + 24.hours
new_case_contact.save!
end
end
# guarantee at least one transition aged youth case to "volunteer1"
volunteer1 = Volunteer.find_by(email: "volunteer1@example.com")
if volunteer1.casa_cases.where(birth_month_year_youth: ..CasaCase::TRANSITION_AGE.years.ago).blank?
rand(1..3).times do
birth_month_year_youth = ((Date.today - 18.year)..(Date.today - CasaCase::TRANSITION_AGE.year)).to_a.sample
new_casa_case = volunteer1.casa_cases.find_or_create_by!(
casa_org_id: volunteer1.casa_org.id,
case_number: generate_case_number,
court_report_submitted_at: court_report_submitted ? Date.today : nil,
court_report_status: court_report_submitted ? :submitted : :not_submitted,
birth_month_year_youth: birth_month_year_youth
)
CourtDate.find_or_create_by!(
casa_case: new_casa_case,
court_report_due_date: court_date + 1.month,
date: court_date
)
end
end
end
end
def create_hearing_types(casa_org)
active_hearing_type_names = [
"emergency hearing",
"trial on the merits",
"scheduling conference",
"uncontested hearing",
"pendente lite hearing",
"pretrial conference"
]
inactive_hearing_type_names = [
"deprecated hearing"
]
active_hearing_type_names.each do |hearing_type_name|
HearingType.find_or_create_by!(
casa_org_id: casa_org.id,
name: hearing_type_name,
active: true
)
end
inactive_hearing_type_names.each do |hearing_type_name|
HearingType.find_or_create_by!(
casa_org_id: casa_org.id,
name: hearing_type_name,
active: false
)
end
end
def create_checklist_items
checklist_item_categories = [
"Education/Vocation",
"Placement",
"Category 3"
]
checklist_item_descriptions = [
"checklist item description 1",
"checklist item description 2",
"checklist item description 3"
]
mandatory_options = [true, false]
half_of_the_hearing_types = HearingType.all.slice(0, HearingType.all.length / 2)
half_of_the_hearing_types.each do |hearing_type|
ChecklistItem.create(
hearing_type_id: hearing_type.id,
description: checklist_item_descriptions.sample,
category: checklist_item_categories.sample,
mandatory: mandatory_options.sample
)
hearing_type.update_attribute(:checklist_updated_date, "Updated #{Time.new.strftime("%m/%d/%Y")}")
end
end
def create_languages(casa_org)
create_language("Spanish", casa_org)
create_language("Vietnamese", casa_org)
create_language("French", casa_org)
create_language("Chinese Cantonese", casa_org)
create_language("ASL", casa_org)
create_language("Other", casa_org)
end
def create_language(name, casa_org)
Language.find_or_create_by!(name: name, casa_org: casa_org)
end
def create_mileage_rates(casa_org)
attempt_count = 5
i = 0
while i < attempt_count
begin
MileageRate.create!({
amount: Faker::Number.between(from: 0.0, to: 1.0).round(2),
effective_date: Faker::Date.backward(days: 700),
is_active: true,
casa_org_id: casa_org.id
})
rescue ActiveRecord::RecordInvalid
attempt_count += 1
end
i += 1
end
end
def create_learning_hour_types(casa_org)
learning_types = %w[book movie webinar conference other]
learning_types.each do |learning_type|
learning_hour_type = casa_org.learning_hour_types.new(name: learning_type.capitalize)
learning_hour_type.position = 99 if learning_type == "other"
learning_hour_type.save
end
end
def create_learning_hour_topics(casa_org)
learning_topics = %w[cases reimbursements court_reports]
learning_topics.each do |learning_topic|
learning_hour_topic = casa_org.learning_hour_topics.new(name: learning_topic.humanize.capitalize)
learning_hour_topic.save
end
end
def create_learning_hours(casa_org)
casa_org.volunteers.each do |user|
[1, 2, 3].sample.times do
learning_hour_topic = casa_org.learning_hour_topics.sample
learning_hour_type = casa_org.learning_hour_types.sample
# randomize between 30 to 180 minutes
duration_minutes = (2..12).to_a.sample * 15
duration_hours = duration_minutes / 60
duration_minutes %= 60
occurred_at = Time.current - (1..7).to_a.sample.days
LearningHour.create(
user:,
learning_hour_type:,
name: "#{learning_hour_type.name} on #{learning_hour_topic.name}",
duration_hours:,
duration_minutes:,
occurred_at:,
learning_hour_topic:
)
end
end
end
private # -------------------------------------------------------------------------------------------------------
def most_recent_past_court_date(casa_case_id)
CourtDate.where(
"date < ? AND casa_case_id = ?",
Date.today,
casa_case_id
).order(date: :desc).first&.date
end
def case_contact_before_last_court_date?(casa_case_id, date)
CaseContact.where(
"occurred_at < ? AND casa_case_id = ?",
date,
casa_case_id
).any?
end
def case_contact_after_last_court_date?(case_case_id, date)
CaseContact.where(
"occurred_at > ? AND casa_case_id = ?",
date,
case_case_id
).any?
end
def order_choices
[
"Limited guardianship of the children for medical and educational purposes to [name] shall be rescinded;",
"The children shall remain children in need of assistance (cina), under the jurisdiction of the juvenile court, and shall remain committed to the department of health and human services/child welfare services, for continued placement on a trial home visit with [NAME]",
"The youth shall continue to participate in educational tutoring, under the direction of the department;",
"The youth shall continue to participate in family therapy with [name], under the direction of the department;",
"The permanency plan for all the children of reunification is reaffirmed;",
"Visitation between the youth and the father shall be unsupervised, minimum once weekly, in the community or at his home, and may include overnights when he has the appropriate space for the children to sleep, under the direction of the department;",
"Youth shall continue to participate in individual therapy, under the direction of the department;",
"The youth shall continue to maintain stable employment;",
"The youth shall maintain appropriate housing while working towards obtaining housing that can accommodate all of the children being reunified, and make home available for inspection, under the direction of the department;",
"The youth shall participate in case management services, under the direction of the department;",
"The youth shall participate in mental health treatment and medication management, under the direction of the department;"
]
end
def transition_aged_youth?(birth_month_year_youth)
(Date.today - birth_month_year_youth).days.in_years > CasaCase::TRANSITION_AGE
end
def base_case_contact_params(casa_case, status)
{
casa_case: casa_case,
creator: casa_case.volunteers.sample(random: rng),
duration_minutes: likely_contact_durations.sample(random: rng),
occurred_at: rng.rand(0..6).months.ago,
contact_types: ContactType.all.sample(2, random: rng),
medium_type: CaseContact::CONTACT_MEDIUMS.sample(random: rng),
miles_driven: rng.rand(5..40),
want_driving_reimbursement: random_true_false,
contact_made: random_true_false,
notes: note_generator,
status: status,
draft_case_ids: [casa_case&.id]
}
end
def random_case_contact_count
@random_case_contact_counts ||= [0, 1, 2, 2, 2, 3, 3, 3, 11, 11, 11]
@random_case_contact_counts.sample(random: rng)
end
def random_past_court_date_count
@random_past_court_date_counts ||= [0, 2, 3, 4, 5]
@random_past_court_date_counts.sample(random: rng)
end
def random_zero_one_count
@random_zero_one_count ||= [0, 1]
@random_zero_one_count.sample(random: rng)
end
def random_court_order_count
@random_court_order_counts ||= [0, 3, 5, 10]
@random_court_order_counts.sample(random: rng)
end
def likely_contact_durations
@likely_contact_durations ||= [15, 30, 60, 75, 4 * 60, 6 * 60]
end
def note_generator
paragraph_count = Random.rand(6)
(0..paragraph_count).map { |index|
Faker::Lorem.paragraph(sentence_count: 5, supplemental: true, random_sentences_to_add: 20)
}.join("\n\n")
end
def generate_case_number
# CINA-YY-XXXX
years = ((DateTime.now.year - 20)..DateTime.now.year).to_a
yy = years.sample(random: rng).to_s[2..3]
@case_number_sequence += 1
"CINA-#{yy}-#{@case_number_sequence}"
end
def generate_court_date
((Date.today + 1.month)..(Date.today + 5.months)).to_a.sample
end
def random_true_false
@true_false_array ||= [true, false]
@true_false_array.sample(random: rng)
end
end
================================================
FILE: db/seeds/default_contact_topics.yml
================================================
- question: "Background information"
details: |-
a) When did the family first come into contact with the Department of Social Services or Department of Juvenile Justice – how many times?
b) Tell the history of their involvement with the department and any facts about their life that could help determine the need for placement and/or services.
c) Discuss the child’s history – behavior problems, educational history, medical history, psychological history (any hospitalizations, previous counseling, etc.)
d) If child has been placed previously give a history of the child’s placements (placed with different parents, relatives, DSS, etc).
- question: "Current situation"
details: |-
a) Where is the child placed?
b) How is the child adjusting to the placement?
c) Are there any issues or concerns about the placement? If so, describe these concerns and specify the actions being taken to address them.
- question: "Education, vocation, or daycare"
details: |-
a) Where is the child placed for education (daycare, public school, non-public school, GED, Job Corps, etc)?
b) How is the child adjusting to the educational placement? Are there any education-related concerns at this point? If yes, detail them and mention the steps taken to address them.
c) Does the child have an IEP? If not, is there a need for one?
d) Is the child employed? If not, are they looking for a job?
e) Does the child have vocational/life skills? Are they attending life skill classes?
f) Are there any other life skill needs? (Driver’s education, state ID, transportation assistance, etc.)
g) What is the feedback from professionals providing these services about the child's progress? Include strengths and not just needs.
- question: "Health and mental health"
details: |-
a) Is the child up to date with medical exams?
b) Are there any other medical concerns?
c) Is the child receiving therapy, medication monitoring, mentoring, or other services? If so, specify with whom these services are being received.
- question: "Family and community connections"
details: |-
a) Is this child seeing parents, siblings, other relatives? If so, who is the child visiting, and how often? Does the child desire a different arrangement?
b) Detail the steps parents have taken to address court orders. Address any barriers and highlight positive steps.
- question: "Child’s strengths"
details: |-
a) Describe the child’s strengths, interests, and hobbies to provide a well-rounded perspective.
================================================
FILE: db/seeds/emancipation_data.rb
================================================
# Emancipation Checklist Form Data
category_housing = EmancipationCategory.where(name: "Youth has housing.").first_or_create(mutually_exclusive: false)
category_housing.add_option("With friend")
category_housing.add_option("With relative")
category_housing.add_option("With former foster parent")
category_housing.add_option("Subsidized (e.g., FUP, Future Bridges, adult services)")
category_housing.add_option("Independently (e.g., renting own apartment or room)")
category_income = EmancipationCategory.where(name: "Youth has income to achieve self-sufficiency.").first_or_create(mutually_exclusive: false)
category_income.add_option("Employment")
category_income.add_option("Public benefits/TCA")
category_income.add_option("SSI")
category_income.add_option("SSDI")
category_income.add_option("Inheritance/survivors benefits")
EmancipationCategory.where(name: "Youth has completed a budget.").first_or_create(mutually_exclusive: false)
category_employment = EmancipationCategory.where(name: "Youth is employed.").first_or_create(mutually_exclusive: true)
category_employment.add_option("Part-time job")
category_employment.add_option("Full-time job")
category_employment.add_option("Apprenticeship or paid internship")
category_employment.add_option("Self-employed")
category_continuing_education = EmancipationCategory.where(name: "Youth is attending an educational or vocational program.").first_or_create(mutually_exclusive: true)
category_continuing_education.add_option("High school")
category_continuing_education.add_option("Post-secondary/college")
category_continuing_education.add_option("Vocational")
category_continuing_education.add_option("GED program")
category_high_school_diploma = EmancipationCategory.where(name: "Youth has a high school diploma or equivalency.").first_or_create(mutually_exclusive: true)
category_high_school_diploma.add_option("Traditional")
category_high_school_diploma.add_option("Out of school program")
category_high_school_diploma.add_option("GED")
category_medical_insurance = EmancipationCategory.where(name: "Youth has medical insurance.").first_or_create(mutually_exclusive: false)
category_medical_insurance.add_option("Has medical insurance card")
category_medical_insurance.add_option("Knows his/her/their primary care entity")
category_medical_insurance.add_option("Knows how to continue insurance coverage")
category_medical_insurance.add_option("Knows that dental insurance ends at age 21")
category_medical_insurance.add_option("Has plan for dental care")
EmancipationCategory.where(name: "Youth can identify permanent family and/or adult connections.").first_or_create(mutually_exclusive: false)
category_community = EmancipationCategory.where(name: "Youth is accessing community activities.").first_or_create(mutually_exclusive: false)
category_community.add_option("Arts activities (e.g., singing, dancing, theater)")
category_community.add_option("Religious affiliations (e.g., church, mosque)")
category_community.add_option("Athletics/team sports")
category_community.add_option("Other")
category_documents = EmancipationCategory.where(name: "Youth has all identifying documents.").first_or_create(mutually_exclusive: false)
category_documents.add_option("Birth certificate (original or certified copy)")
category_documents.add_option("Social security card")
category_documents.add_option("Learner's permit")
category_documents.add_option("Driver's license")
category_documents.add_option("Immigration documents")
category_documents.add_option("State identification card")
category_transportation = EmancipationCategory.where(name: "Youth has access to transportation.").first_or_create(mutually_exclusive: false)
category_transportation.add_option("Vehicle")
category_transportation.add_option("Public transportation")
category_juvenile_criminal_cases = EmancipationCategory.where(name: "Youth has been or is involved in past or current juvenile cases.").first_or_create(mutually_exclusive: false)
category_juvenile_criminal_cases.add_option("All juvenile issues have been resolved.")
category_juvenile_criminal_cases.add_option("All eligible juvenile records have been expunged.")
category_adult_criminal_cases = EmancipationCategory.where(name: "Youth has been or is involved in past or current adult criminal cases.").first_or_create(mutually_exclusive: false)
category_adult_criminal_cases.add_option("All adult criminal cases have been resolved.")
category_adult_criminal_cases.add_option("All eligible adult criminal records have been expunged.")
EmancipationCategory.where(name: "Youth has been or is involved in civil or family cases.").first_or_create(mutually_exclusive: false)
category_bank_account = EmancipationCategory.where(name: "Youth has a bank account in good standing.").first_or_create(mutually_exclusive: false)
category_bank_account.add_option("Checking")
category_bank_account.add_option("Savings")
category_credit = EmancipationCategory.where(name: "Youth has obtained a copy of his/her/their credit report.").first_or_create(mutually_exclusive: false)
category_credit.add_option("An adult has reviewed the credit report with Youth.")
category_credit.add_option("Issues or concerns")
EmancipationCategory.where(name: "Youth can identify his/her/their core values.").first_or_create(mutually_exclusive: false)
EmancipationCategory.where(name: "Youth has completed the Ansell Casey Assessment.").first_or_create(mutually_exclusive: false)
================================================
FILE: db/seeds/emancipation_options_prune.rb
================================================
def get_category_by_name(category_name)
EmancipationCategory.where(name: category_name)&.first
end
get_category_by_name("Youth has completed a budget.")&.delete_option("Completed budget")
get_category_by_name("Youth is employed.")&.delete_option("Not employed")
get_category_by_name("Youth is attending an educational or vocational program.")&.delete_option("Not attending")
get_category_by_name("Youth has a high school diploma or equivalency.")&.delete_option("No")
get_category_by_name("Youth can identify permanent family and/or adult connections.")&.delete_option("Has connections")
get_category_by_name("Youth has been or is involved in civil or family cases.")&.delete_option("All civil or family cases have been resolved.")
get_category_by_name("Youth can identify his/her/their core values.")&.delete_option("Identified values")
get_category_by_name("Youth has completed the Ansell Casey Assessment.")&.delete_option("Threshold for self-sufficiency was met.")
get_category_by_name("Youth has completed the Ansell Casey Assessment.")&.update_attribute(:name, "Youth has completed the Ansell Casey Assessment and threshold for self-sufficiency was met.")
================================================
FILE: db/seeds/patch_note_group_data.rb
================================================
# PatchNote Section Headers
PatchNoteGroup.where(value: "CasaAdmin+Supervisor").first_or_create
PatchNoteGroup.where(value: "CasaAdmin+Supervisor+Volunteer").first_or_create
================================================
FILE: db/seeds/patch_note_type_data.rb
================================================
# PatchNote Section Headers
PatchNoteType.where(name: "Coming Up").first_or_create
PatchNoteType.where(name: "Fixes").first_or_create
PatchNoteType.where(name: "What's New?").first_or_create
================================================
FILE: db/seeds/placement_data.rb
================================================
casa_orgs = CasaOrg.all
placement_types = [
"Reunification",
"Custody/Guardianship by a relative",
"Custody/Guardianship by a non-relative",
"Adoption by relative",
"Adoption by a non-relative",
"APPLA"
]
casa_orgs.each do |org|
placement_types.each do |label|
PlacementType.where(name: label, casa_org: org).first_or_create
end
end
================================================
FILE: db/seeds.rb
================================================
# This seed script populates the development DB with a data set whose size is dependent on the Rails environment.
# You can control the randomness of the data provided by FAKER and the Rails libraries via the DB_SEEDS_RANDOM_SEED environment variable.
# If you specify a number, that number will be used as the seed, so you can enforce consistent data across runs
# with nondefault content.
# If you specify the string 'random' (e.g. `export DB_SEEDS_RANDOM_SEED=random`), a random seed will be assigned for you.
# If you don't specify anything, 0 will be used as the seed, ensuring consistent data across hosts and runs.
require_relative "seeds/casa_org_populator_presets"
require_relative "seeds/db_populator"
require_relative "../lib/tasks/data_post_processors/case_contact_populator"
require_relative "../lib/tasks/data_post_processors/contact_type_populator"
require_relative "../lib/tasks/data_post_processors/sms_notification_event_populator"
require_relative "../lib/tasks/data_post_processors/contact_topic_populator"
class SeederMain
attr_reader :db_populator, :rng
def initialize
random_seed = get_seed_specification
@rng = Random.new(random_seed) # rng = random number generator
@db_populator = DbPopulator.new(rng)
Faker::Config.random = rng
Faker::Config.locale = "en-US" # only allow US phone numbers
end
def seed
log "NOTE: CASA seed does not delete anything anymore! Run rake db:seed:replant to delete everything and re-seed"
log "Creating the objects in the database..."
db_populator.create_all_casa_admin("allcasaadmin@example.com")
db_populator.create_all_casa_admin("all_casa_admin1@example.com")
db_populator.create_all_casa_admin("admin1@example.com")
options1 = OpenStruct.new(CasaOrgPopulatorPresets.for_environment.merge({org_name: "Prince George CASA"}))
org1 = db_populator.create_org(options1)
create_org_related_data(db_populator, org1, options1)
options2 = OpenStruct.new(CasaOrgPopulatorPresets.minimal_dataset_options)
org2 = db_populator.create_org(options2)
create_org_related_data(db_populator, org2, options2)
SmsNotificationEventPopulator.populate
2.times do
options3 = OpenStruct.new(CasaOrgPopulatorPresets.minimal_dataset_options)
org3 = DbPopulator.new(rng, case_fourteen_years_old: true)
.create_org(options3)
create_org_related_data(db_populator, org3, options3)
end
post_process_data
report_object_counts
log "\nDone.\n\n"
end
private # -------------------------------------------------------------------------------------------------------
# Used for reporting record counts after completion:
def active_record_classes
@active_record_classes ||= [
AllCasaAdmin,
CasaAdmin,
CasaOrg,
CasaCase,
CaseContact,
ContactTopic,
ContactTopicAnswer,
CaseCourtOrder,
CaseAssignment,
ChecklistItem,
CourtDate,
ContactType,
ContactTypeGroup,
HearingType,
Judge,
Language,
LearningHourType,
LearningHourTopic,
MileageRate,
OtherDuty,
Supervisor,
SupervisorVolunteer,
User,
LearningHour,
Volunteer,
PlacementType
]
end
def post_process_data
ContactTypePopulator.populate
CaseContactPopulator.populate
ContactTopicPopulator.populate
end
def get_seed_specification
seed_environment_value = ENV["DB_SEEDS_RANDOM_SEED"]
if seed_environment_value.blank?
seed = 0
log "\nENV['DB_SEEDS_RANDOM_SEED'] not set to 'random' or a number; setting seed to 0.\n\n"
elsif seed_environment_value.casecmp("random") == 0
seed = Random.new_seed
log "\n'random' specified in ENV['DB_SEEDS_RANDOM_SEED']; setting seed to randomly generated value #{seed}.\n\n"
else
seed = seed_environment_value.to_i
log "\nUsing random seed #{seed} specified in ENV['DB_SEEDS_RANDOM_SEED'].\n\n"
end
seed
end
def report_object_counts
log "\nRecords written to the DB:\n\nCount Class Name\n----- ----------\n\n"
active_record_classes.each do |klass|
log format("%5d %s", klass.count, klass.name)
end
log "\n\nVolunteers, Supervisors and CasaAdmins are types of Users"
end
def log(message)
return if Rails.env.test?
Rails.logger.debug { message }
end
def create_org_related_data(db_populator, casa_org, options)
db_populator.create_users(casa_org, options)
db_populator.create_cases(casa_org, options)
db_populator.create_hearing_types(casa_org)
db_populator.create_checklist_items
db_populator.create_judges(casa_org)
db_populator.create_languages(casa_org)
db_populator.create_mileage_rates(casa_org)
db_populator.create_learning_hour_types(casa_org)
db_populator.create_learning_hour_topics(casa_org)
db_populator.create_learning_hours(casa_org)
db_populator.create_other_duties
end
end
SeederMain.new.seed
load(Rails.root.join("db/seeds/emancipation_data.rb"))
begin
load(Rails.root.join("db/seeds/emancipation_options_prune.rb"))
rescue => e
Rails.logger.error { "Caught error during db seed emancipation_options_prune, continuing. Message: #{e}" }
end
load(Rails.root.join("db/seeds/placement_data.rb"))
load(Rails.root.join("db/seeds/api_credential_data.rb"))
================================================
FILE: doc/API.md
================================================
### POST `/casa_cases.json`
Creates a casa case
### Params:
- **casa_case**
Required. Contains all the object containing all the casa case params
- **case_number**: "CINA-123-ABC",
Required. A unique string to identify the casa case
- **transition_aged_youth**: true,
A boolean marking the case as transitioning or not. Currently in the process of deprecating this field
- **birth_month_year_youth**: "2007-10-21",
Required. A date in the format YYYY-MM-DD determining if the case as transitioning or not.
- **casa_org_id**: 1,
Required. The id of the casa org of the case.
- **hearing_type_id**: 1,
The id of the hearing type for the next court date.
- **judge_id**: 1
The id of the case judge
### POST `/case_assignments.json`
Creates a case_assignment
- **Params:**
- **casa_case_id**: 1,
Required. The id of the casa case the volunteer is being assigned to.
- **volunteer_id**: 1,
Required. The id of the volunteer being assigned to the casa case.
### PATCH `/case_assignments/:id/unassign.json`
Unassigns a case_assignment
- **Params:**
- **id**: 1,
Required. The id of the case_assignment to be unassigned.
================================================
FILE: doc/CONTRIBUTING.md
================================================
# Contributing
We ♥ contributors! By participating in this project, you agree to abide by the Ruby for Good [code of conduct](https://github.com/rubyforgood/code-of-conduct).
If you have any questions about an issue, comment on the issue, open a new issue or ask in [the RubyForGood slack](https://join.slack.com/t/rubyforgood/shared_invite/zt-35218k86r-vlIiWqig54c9t~_LkGpQ7Q). CASA has a `#casa` channel in the Slack. Our channel in slack also contains a zoom link for office hours every day office hours are held.
You won't be yelled at for giving your best effort. The worst that can happen is that you'll be politely asked to change something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
## Contributing Steps
### Issues
All work is organized by issues.
[Find issues here.](https://github.com/rubyforgood/projects/9)
If you would like to contribute, please ask for an issue to be assigned to you.
If you would like to contribute something that is not represented by an issue, please make an issue and assign yourself.
Only take multiple issues if they are related and you can solve all of them at the same time with the same pull request.
### Pull Requests
If you are so inclined, you can open a draft PR as you continue to work on it.
1. Follow [the setup guide](https://github.com/rubyforgood/casa#installation) to get the project working locally.
1. We only accept pull requests with passing tests. To ensure your setup is working before you get started it's great to run the tests first.
- To run the tests use: `bundle exec rspec`
1. Add a test for your change. If you are adding functionality or fixing a bug, you should add a test!
1. Run linters and fix any linting errors that come up.
- From the repo root run: `./bin/git_hooks/lint`
1. Push to your branch/fork and submit a pull request. Include the issue number (ex. `Resolves #1`) in the PR description. This will ensure the issue gets closed automatically when the pull request gets merged.
#### Pull Request Checks
We will try to respond to your PR quickly.
There are scripts that check the code to ensure the code is working. Most of them need to pass. You can see the scripts run at the bottom of your pull request webpage (learn more about the scripts [here](https://github.com/rubyforgood/casa/wiki/Pull-Request-Checks)). You should attempt to fix errors found in the automated testing.
Pull requests are also manually reviewed. We may request changes after a manual review.
Some qualities of good pull requests:
- Small line diff count. Several small pull requests for a large issue are preferred over one big pull request.
- Include tests that fail without your code, and pass with it.
- For pull requests changing UI, make sure the UI matches the rest of the site. Some of our users aren't great with computers and we don't want to make them learn new things if we don't need to.
- Update the documentation, for things like new rails/bash commands. Please include a guide if modifying the code in the future is difficult. For example [editing .docx templates](https://github.com/rubyforgood/casa/wiki/How-to-edit-docx-templates---word-document-court-report) is difficult because the documentation is hard to find and it requires microsoft word.
- If your pull request involves user permissions, use [policy files](https://github.com/varvet/pundit#policies).
- If your pull request has an erb file with complex rails logic inside of it, please use a [decorator](https://medium.com/@kosovacsedad/ruby-on-rails-decorator-design-pattern-b54a1afd03c8).
================================================
FILE: doc/DOCKER.md
================================================
# Development setup using Docker
After you install Docker, please follow either the automatic setup or the manual
setup. If you are new to Docker, it is recommended that you follow the manual
setup.
## Installing Docker
Install [Docker Community Edition](https://docs.docker.com/install/) if it is not already installed.
## Automatic Setup
The automatic setup explained here relies on Bash scripts in the docker directory to execute the most basic and frequent tasks in Docker. There is substantially less typing to do under the automatic setup than under the manual setup.
### Initial setup
1. Clone the repository to your local machine: `git clone https://github.com/rubyforgood/casa.git` or create a fork in GitHub if you don't have permission to commit directly to this repo.
2. Change into the application directory: `cd casa`
3. Run `docker/build` to build the app, seed the database, run the local web server (in a detached state), run the test suite, and log the screen outputs of these processes in the log directory. The web application will be available at http://localhost:3000.
4. Run `docker/test` to run the test suite and log the screen output in the log directory.
5. If you reboot the machine, restart Docker, or stop any services, the tests and many other functions will not work. Please run `docker/server` to restart the app and allow the tests and other functions to work.
### Other Automated Scripts
* Run `docker/seed` to reseed the database.
* Run `docker/server` to restart the local web server (in a detached state).
* Run `docker/nukec` to delete all of the Docker containers.
* Run `docker/nuke` to delete all Docker containers, Docker networks, and Docker images.
* Run `docker/console` to start the Rails Console.
* Run `docker/sandbox` to start the Rails Sandbox.
* Run `docker/brakeman` to run the Brakeman security tool, which checks for security vulnerabilities.
* Use the `docker/run` script to run any command within the Rails Docker container. For example, entering `docker/run cat /etc/os-release` executes the command `cat /etc/os-release` within the Rails Docker container.
## Manual Setup
The manual setup instructions walk you through building the images and starting
the containers using Docker Compose commands directly. This setup method is particularly
recommended if you are new to Docker.
### Initial setup
The following commands should just be run for the initial setup only. Rebuilding the docker images is only necessary when upgrading, if there are changes to the Dockerfile, or if gems have been added or updated.
1. Clone the respository to your local machine: `git clone https://github.com/rubyforgood/casa.git` or create a fork in GitHub if you don't have permission to commit directly to this repo.
2. Change into the application directory: `cd casa`
3. Run `docker compose build` to build images for all services.
4. Run `docker compose run --rm web bundle install` to install ruby dependencies
5. Run `docker compose run --rm web rails db:reset` to create the dev and test databases, load the schema, and run the seeds file.
6. Run `docker compose run --rm web npm install` to install javascript dependencies
7. Run `docker compose run --rm web npm run build` to bundle javascript assets
8. Run `docker compose run --rm web npm run build:css` to bundle the css
9. Run `docker compose up` to start all the remaining services. Or use `docker compose up -d` to start containers in the background.
10. Run `docker compose ps` to view status of the containers. All should have state "Up". Check the [logs](#viewing-logs) if there are any containers that did not start.
11. The web application will be available at http://localhost:3000
### For ongoing development:
* Run `docker compose up -d` to start all services.
* Run `docker compose ps` to view status of containers.
* Run `docker compose stop` to stop all services.
* Run `docker compose restart web` to restart the web server.
* Run `docker compose rm ` to remove a stopped container.
* Run `docker compose rm -f ` to force remove a stopped container.
* Run `docker compose up -d --force-recreate` to start services with new
containers.
* Run `docker compose build web` to build a new image for the web service.
After re-building an image, run `docker compose up -d --force-recreate web`
to start a container running the new image.
* Run `docker compose down -v` to stop and remove all containers, as well as
volumes and networks. This command is helpful if you want to start with a
clean slate. However, it will completely remove the database and you will
need to go through the database setup steps again above.
#### Running commands
In order to run rake tasks, rails generators, bundle commands, etc., they need to be run inside the container:
```
$ docker compose exec web rails db:migrate
```
If you do not have the web container running, you can run a command in a one-off container:
```
$ docker compose run --rm web bundle install
```
However, when using a one-off container, make sure the image is up-to-date by
running `docker compose build web` first. If you have been making gem updates
to your container without rebuilding the image, then the one-off container will
be out of date.
#### Running webpack dev server
To speed compiling of assets, run the webpack dev server in a separate terminal
window:
```
$ docker compose exec web bin/webpack-dev-server
```
#### Viewing logs
To view the logs, run:
```
$ docker compose logs -f
```
For example:
```
$ docker compose logs -f web
```
#### Accessing services
##### Postgres database
```
$ docker compose exec database psql -h database -Upostgres casa_development
```
##### Rails console
```
$ docker compose exec web rails c
```
### Testing Suite
Run the testing suite from within the container:
```
$ docker compose exec web rspec spec -fd
```
For a shorter screen output from running the testing suite from within the container:
```
$ docker compose exec web rspec spec
```
System tests will generate a screenshot upon failure. The screenshots can be
found in the local `tmp/screenshots` directory which maps to the
`/usr/src/app/tmp/screenshots` directory inside the container.
#### Watching tests run
You can view the tests in real time by using a VNC client and temporarily
switching to the `selenium_chrome_in_container` driver set in
[spec/spec_helper.rb](https://github.com/rubyforgood/casa/blob/master/spec/spec_helper.rb).
For example, you can change this:
```
if ENV["DOCKER"]
driven_by :selenium_chrome_headless_in_container
```
to this:
```
if ENV["DOCKER"]
# driven_by :selenium_chrome_headless_in_container
` driven_by :selenium_chrome_in_container
```
Mac OS comes with a built-in screen sharing application, "Screen Sharing".
On Ubuntu-based Linux, the VNC client application "Vinagre" (aka "Remote Desktop Viewer")
is commonly used, and can be installed with `sudo apt install vinagre`.
You can open the VNC client application and configure it directly, but in both operating systems
it's probably easier to click on [vnc://localhost:5900](vnc://localhost:5900)
(or paste that into your browser's address bar) and let the browser launch the VNC client with
the appropriate parameters for you.
The VNC password is `secret`.
Run the spec(s) from the command line and you can see the test running in the browser through the VNC client.
## Troubleshooting
### Nokogiri not found on some macs
https://stackoverflow.com/questions/70963924/unable-to-load-nokogiri-in-docker-container-on-m1-mac
================================================
FILE: doc/LINUX_SETUP.md
================================================
# Linux Development Environment Installation
The commands below can be run all at once by copying and pasting them all into a file and running the file as a script
(e.g. `bash -x script_name`).
If you copy and paste directly from this page to your command line, we recommend you do so one section (or even one line) at a time.
```
# Install Linux Packages
sudo apt update # Check internet for updates
sudo apt upgrade -y # Install updates
sudo apt install -y git # In case you don't have it already
sudo apt install -y libvips42 # Render images for your local web server
sudo apt install -y libpq-dev # Helps compile C programs to be able to communicate with postgres
# Optional
sudo apt install -y curl # A command to help fetching and sending data to urls
sudo apt install -y vim # A text editor accessible from the command line
```
```
# Install Postgres
# Add the postgres repo
# Create the file repository configuration:
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
# Add the repo key to your keyring:
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | sudo tee /usr/share/keyrings/postgres-archive-keyring.gpg
# This next step is done to limit the key to only the postgres repo
# Otherwise the signing key is considered valid for all your enabled Debian repositories
# Open /etc/apt/sources.list.d/pgdg.list with super user permissions so you are allowed to write to the file
# Example using vim:
# sudo vim /etc/apt/sources.list.d/pgdg.list
# Paste "[signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg]" between "deb" and "http://apt.postgresql..."
# Example: deb [signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt noble-pgdg main
# Save the file
# Update the package lists:
sudo apt update
# Install Postgres 12
sudo apt install -y postgresql-12
# Turn the server on
sudo systemctl start postgresql@12-main
# Add user to Postgres:
sudo -u postgres psql -c "CREATE USER $USER WITH CREATEDB"
# See https://www.postgresql.org/download/linux/ubuntu/ for more details
```
```
# Install NVM and Node JS
# you can use curl
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
# or wget
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
# Restart your terminal
# List all available LTS versions
nvm ls-remote | grep -i 'Latest LTS'
# Install version from .nvmrc file:
nvm install
# Update npm
npm i -g npm@latest
```
```
# add node and node tools to the path
nvm alias default lts/krypton
```
```
# Install and configure rbenv
sudo apt install libyaml-dev
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
~/.rbenv/bin/rbenv init
# Restart your terminal
# fetch list of ruby versions
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
rbenv install 4.0.2
```
If you would like RVM instead of rbenv
```
# Install RVM (Part 1)
gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash
. ./.bashrc
rvm get head
rvm install 4.0.2
rvm alias create ruby 4.0.2
rvm alias create default ruby-4.0.2
```
```# Download the Chrome browser (for RSpec testing):
sudo curl -sS -o - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt -y update
sudo apt -y install google-chrome-stable
```
## Connecting to Github via ssh
Connecting to Gihub via ssh prevents being required to login very often when using git commands.
### Creating an SSH Key Pair
- Open Terminal.
- Paste the text below, substituting in your GitHub email address.
`ssh-keygen -t ed25519 -C "your_email@example.com"`
- For all prompts simply press enter to set default values.
#### Adding your SSH key to the ssh-agent
- Run `eval "$(ssh-agent -s)"` in your terminal to start the ssh-agent in the background. It will use very few resources.
- Run `ssh-add ~/.ssh/id_ed25519` to add your private key to the ssh agent.
See [github's article](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) for more details/updates.
### Add your ssh key to your github account.
[See github's guide](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account)
### Test Your ssh Connection
- Run `ssh -T git@github.com`
- If you see `Are you sure you want to continue connecting (yes/no)?`, enter `yes`
- If you see `Hi username! You've successfully authenticated, but GitHub does not provide shell access.`, the connection is set up correctly.
## Project Installation
`cd` to the directory where you would like to install CASA
Run this series of commands to install the project.
```
git clone git@github.com:rubyforgood/casa.git # Download a copy of the repository locally
# The URI will be different when cloning a fork
cd casa # Go into the folder containing casa
bundle install # Install ruby dependencies
bundle exec rails db:setup # Create your local test database
bundle exec rails db:migrate # Update the database if it's out of date
bundle exec rake after_party:run # Run post deployment tasks
npm install # install javascript dependencies
npm run build # compile javascript
npm run build:css # compile css
```
[Back to the main readme for steps to test your installation.](https://github.com/rubyforgood/casa#running-the-app--verifying-installation)
================================================
FILE: doc/MAC_SETUP.md
================================================
# Install Needed Dependencies
## Homebrew
If you haven't already, install the [homebrew](https://brew.sh/) package manager.
## Postgres
Use homebrew to install and run postgresql:
```bash
brew install postgresql
```
```bash
brew services start postgresql
```
If you have an older version of postgres, `brew postgresql-upgrade-database`
For a more GUI focused postgres experience, try [Postgres.app](https://postgresapp.com/) an alternative to the CLI focused default postgres
If you are having trouble connecting to your local postgres database using pgAdmin or another local tool, try the following configuration:
```
Host Name: localhost
Port: 5432
Maintenance Database: postgres
Username: you_mac_login_username (Can be found by calling whoami in a terminal)
Password: password
```
## Ruby
### Rbenv
It is often useful to install Ruby with a ruby version manager. The version of Ruby that comes with Mac is not sufficient
for this project. You can install [rbenv](https://github.com/rbenv/rbenv) with:
```bash
brew install rbenv ruby-build
```
Then, setup rbenv:
```bash
rbenv init
```
And finally, follow the setup instructions that are outputted to your terminal after running that.
### Actually installing Ruby
Next, install the version of Ruby that this project uses. This can be found by checking the file in this repo, `.ruby-version`.
To install the appropriate ruby version, run:
```bash
rbenv install 4.0.2
```
(Do not forget to switch 4.0.2 to the appropriate version)
Finally, run:
```bash
rbenv local 4.0.2
```
(Do not forget to switch 4.0.2 to the appropriate version)
## Nodejs
The Casa package frontend leverages several javascript packages managed through `npm`.
```bash
brew install node
```
## Chrome
Many of the frontend tests are run using Google Chrome, so if you don't already have that installed you may wish to include it:
```bash
brew install google-chrome
```
## Project setup
Install gem dependencies with:
```bash
bundle install
```
Setup the database with:
```bash
bin/rails db:setup
```
Install javascript dependencies with:
```bash
npm install
```
Compile assets with:
```bash
npm run build
```
and then:
```bash
npm run build:css
```
And lastly, run the app with:
```bash
bin/rails server
```
See the README for login information.
================================================
FILE: doc/NIX_SETUP.md
================================================
**Nix**
If you have [Nix](https://nixos.org) installed you can use the [flake.nix](flake.nix)
configuration file located at the root of the project to build and develop within an environment
without needing to install `rvm`, `nodejs`, `postgresql` or other tools separately.
The environment also uses the `gemset.nix` file to automatically download and install all the gems
necessary to get the server up and running:
1. Install [Nix](https://zero-to-nix.com/concepts/nix-installer)
2. Add the following to `~/.config/nix/nix.conf` or `/etc/nix/nix.conf`:
```
experimental-features = nix-command flakes
```
3. `cd` into casa
4. `nix-shell -p bundix --run "bundix -l"` to update the `gemset.nix` file
5. `nix develop` and wait for the packages to be downloaded and the environment to be built
Then you can setup the database and run the server.
This will run on Linux and macOS.
================================================
FILE: doc/SECURITY.md
================================================
# Security Policy
## Supported Versions
Only the latest version [main branch](https://github.com/rubyforgood/casa) and currently deployed version of this project is in scope for security issues. Also, only the production environment is in scope, although it's ok and normal to test the staging environment.
## Reporting a Vulnerability
Please report a vulnerability by emailing casa@rubyforgood.org
You can also open a github issue (do NOT provide vulnerability details on github) to notify us that you need to report an issue.
We will reply to all reported issues within a week and update at least every two days.
We currently do not have any bug bounty program but we will be happy to list your name in our contributors list! :)
================================================
FILE: doc/WSL_SETUP.md
================================================
This guide will walk you through setting up the neccessary environment using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) (Windows Subsystem for Linux), which will allow you to run Ubuntu on your Windows machine.
You will need the following local tools installed:
1. WSL
2. Ruby
3. NodeJs (optional)
4. Postgres
5. Google Chrome
### WSL (Windows Subsystem for Linux)
1. **Install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)**.
`wsl --install`
The above command only works if WSL is not installed at all, if you run `wsl --install `and see the WSL help text, do `--install -d Ubuntu`
2. **Run Ubuntu on Windows**
You can run Ubuntu on Windows [several different ways](https://docs.microsoft.com/en-us/windows/wsl/install#ways-to-run-multiple-linux-distributions-with-wsl), but we suggest using [Windows Terminal](https://docs.microsoft.com/en-us/windows/terminal/install).
To open an Ubuntu tab in Terminal, click the downward arrow and choose 'Ubuntu'.
The following commands should all be run in an Ubuntu window.
### Ruby
Install a ruby version manager like [rbenv](https://github.com/rbenv/rbenv#installation)
**Be sure to install the ruby version in `.ruby-version`. Right now that's Ruby 4.0.2.**
Instructions for rbenv:
1. **Install rbenv**
`sudo apt install rbenv`
2. **Set up rbenv in your shell**
`rbenv init`
3. **Close your Terminal window and open a new one so your changes take effect.**
4. **Verify that rbenv is properly set up**
`curl -fsSL https://github.com/rbenv/rbenv-installer/raw/main/bin/rbenv-doctor | bash`
5. **[Install Ruby](https://github.com/rbenv/rbenv#installing-ruby-versions)**
**Be sure to install the ruby version in `.ruby-version`. Right now that's Ruby 4.0.2.**
`rbenv install 4.0.2`
6. **Set a Ruby version to finish installation and start**
`rbenv global 4.0.2` OR `rbenv local 4.0.2`
#### Troubleshooting
If you are on Ubuntu in Windows Subsystem for Linux (WSL) and `rbenv install` indicates that the Ruby version is unavailable, you might be using Ubuntu's default install of `ruby-build`, which only comes with old installs of Ruby (ending before 2.6.) You should uninstall rvm and ruby-build's apt packages (`apt remove rvm ruby-build`) and install them with Git like this:
- `git clone https://github.com/rbenv/rbenv.git ~/.rbenv`
- `echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc`
- `echo 'eval "$(rbenv init -)"' >> ~/.bashrc`
- `exec $SHELL`
- `git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build`
You'll probably hit a problem where ruby-version reads `ruby-2.7.2` but the install available to you is called `2.7.2`. If you do, install [rbenv-alias](https://github.com/tpope/rbenv-aliases) and create an alias between the two.
### NodeJS
The Casa package frontend leverages several javascript packages managed through `npm`, so if you are working on those elements you will want to have node, npm.
1. **(Recommended) [Install nvm](https://github.com/nvm-sh/nvm#installing-and-updating)**
NVM is a node version manager.
### Postgres
1. **[Install PostgresSQL](https://docs.microsoft.com/en-us/windows/wsl/tutorials/wsl-database#install-postgresql) for WSL**
`sudo apt install postgresql postgresql-contrib` - install
`psql --version` - confirm installation and see version number
2. **Install libpq-dev library**
`sudo apt-get install libpq-dev`
3. **Start your postgresql service**
`sudo service postgresql start`
### Google Chrome
Many of the frontend tests are run using Google Chrome, so if you don't already have that installed you may wish to install it.
For some linux distributions, installing `chromium-browser` may be enough on WSL. However, some versions of Ubuntu may require the chromium snap to be installed in order to use chromium.
If you receive errors about needing the chromium snap while running the test suite, you can install Chrome and chromedriver instead:
1. Download and Install Chrome on WSL Ubuntu
- Update your packages:
`sudo apt update`
- Download Chrome:
`wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb`
- Install chrome from the downloaded file
`sudo dpkg -i google-chrome-stable_current_amd64.deb`
`sudo apt-get install -f`
- Check that Chrome is installed correctly
`google-chrome --version`
2. Install the appropriate Chromedriver
- Depending on the version of google-chrome you installed, you will need a specific chromedriver. You can see which version you need on the [chromedriver dowload page](https://chromedriver.chromium.org/downloads).
- For example, if `google-chrome --version` returns 105.x.xxxx.xxx you will want to download the version of chromedriver recommended on the page for version 105.
- As of this writing, the download page says the following for Chrome version 105:
> If you are using Chrome version 105, please download ChromeDriver 105.0.5195.52
- To download chromedriver, run the following command, replacing `{CHROMEDRIVER-VERSION}` with the version of chromedriver you need (e.g., 105.0.5195.52)
`wget https://chromedriver.storage.googleapis.com/{CHROMEDRIVER-VERSION}/chromedriver_linux64.zip`
- Next, unzip the file you downloaded
`unzip chromedriver_linux64.zip`
- Finally, move chromedriver to the correct location and enable it for use:
`sudo mv chromedriver /usr/bin/chromedriver`
`sudo chown root:root /usr/bin/chromedriver`
`sudo chmod +x /usr/bin/chromedriver`
3. Run the test suite
- Assuming the rest of the application is already set up, you can run the test suite to verify that you no longer receive error regarding chromium snap:
`bin/rails spec`
### Casa & Rails
Casa's install will also install the correct version of Rails.
1. **Download the project**
**You should create a fork in GitHub if you don't have permission to directly commit to this repo. See our [contributing guide](https://github.com/rubyforgood/casa/blob/main/doc/CONTRIBUTING.md) for more detailed instructions.**
`git clone ` - use your fork's address if you have one
ie
`git clone https://github.com/rubyforgood/casa.git`
2. **Installing Packages**
`cd casa/`
`bundle install` - install ruby dependencies.
`npm install` - install javascript dependencies.
3. **Database Setup**
Be sure your postgres service is running (`sudo service postgresql start`).
Create a postgres user that matches your Ubuntu user:
`sudo -u postgres createuser ` - create user
`sudo -u postgres psql` - logs in as the postgres user
`psql=# alter user with encrypted password '';` - add password
`psql=# alter user CREATEDB;` - give permission to your user to create databases
Set up the Casa DB
`bin/rails db:setup` - sets up the db
`bin/rails db:seed:replant` - generates test data (can be rerun to regenerate test data)
4. **Compile Assets**
- `npm run build` compile javascript
`npm run build:dev` to auto recompile for when you edit js files
- `npm run build:css` compile css
`npm run build:css:dev` to auto recompile for when you edit sass files
### Getting Started
See [Running the App / Verifying Installation](https://github.com/rubyforgood/casa#running-the-app--verifying-installation).
A good option for editing files in WSL is [Visual Studio Code Remote- WSL](https://code.visualstudio.com/docs/remote/wsl)
================================================
FILE: doc/architecture-decisions/0001-record-architecture-decisions.md
================================================
# 1. Record architecture decisions
Date: 2020-04-04
## Status
Accepted
## Context
We need to record the architectural decisions made on this project.
## Decision
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
## Consequences
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
================================================
FILE: doc/architecture-decisions/0002-disallow-ui-sign-ups.md
================================================
# 2. Disallow user sign-ups from the UI
Date: 2020-04-04
## Status
Accepted
## Context
We want it to be easy for people to join the organization, however we don't want random people signing up and spamming us. We want admin users to have control over who has accounts on the system. We don't have the capacity to handle this properly through the user interface right now.
## Decision
We are going to disable Devise 'registerable' for the user model so that there will no longer be a public sign up option on the site. Creation of new accounts will be done on the backend.
## Consequences
Admins have to do more work to sign up users, but this gives them more control over who can access the site.
================================================
FILE: doc/architecture-decisions/0003-multiple-user-tables.md
================================================
# 3. Having 2 user tables
Date: 2020-04-05
## Status
Accepted
## Context
This is planned to be a multi-tenant system. There will be multiple CASA orgs in the system, so every case, case_contact, volunteer, supervisor, casa_admin etc must have a casa_org_id, because no one is allowed to belong to multiple CASAs. Volunteer, supervisor, and casa_admin are all roles for a "User" db object. In addition to those existing roles, we want to create a new kind of user: all_casa_admin. We need to handle the case of super users who have access to multiple casa_orgs, so they would be difficult to handle in the existing User table--with null handling around their casa_org_id field. We have used the built-in Devise ability to have multiple user tables, as recommended to us by our Rails expert Betsy. This is to prevent needing null handling around casa_id for User records since all_casa_admin users will not have casa_id populated.
Additionally, all_casa_admin users are currently intended to be allowed to create casa_admin users, but NOT to be able to see or edit any CASA data like volunteer assignments, cases, case_updates etc.
## Decision
We are using two tables for users: "user" table for volunteers,supervisors, and casa_admin (all of which must have a casa_id). "all_casa_admin" for all_casa_admins, which will have no casa_id.
## Consequences
The login behavior and dashboard page for all_casa_admin will need to be created and handled separately from the regular user login and dashboard
================================================
FILE: doc/architecture-decisions/0004-use-bootstrap.md
================================================
# 1. Use Bootstrap for styling
Date: 2020-04-05
## Status
Proposed
## Context
We would like to have an easy-to-use system for consistent styles that doesn't
take much tinkering. We propose using the `bootstrap` gem.
## Decision
Pending
## Consequences
Some familiarity with
[Bootstrap](https://getbootstrap.com/docs/4.4/getting-started/introduction/)
will likely be necessary but we hope the medium-to-long term benefits will
outweigh the cost to learn up front.
================================================
FILE: doc/architecture-decisions/0005-android-app-as-a-pwa.md
================================================
# 1. The android app is a [PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) running in a [TWA](https://developer.chrome.com/docs/android/trusted-web-activity/overview/)
Date: 2021-07-07
## Context
Building a [progressive web app(PWA)](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) is a very quick way to convert a website into a mobile app for android.
PWAs can support offline mode and push notifications.
Our app runs in a [trusted web activity(TWA)](https://developer.chrome.com/docs/android/trusted-web-activity/overview/) which is very similar to having the web page load in a mobile browser. The trusted web activity offers browser like support for the PWA.
## Consequences
More javascript support for [service workers](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers) to support offline mode.
[Maintaining a key for app signing](https://github.com/rubyforgood/casa-android/wiki/How-to-manage-app-signing)
================================================
FILE: doc/architecture-decisions/0006-few-controller-tests.md
================================================
# 1. System tests are preferred over controller tests
Date: 2021-07-14
"system" tests test both the erb and the controller at the same time. They are slower. They use capybara. Having some of these (one per rendered page) is very important because it is possible for a controller to define a variable `@a` and an erb to require a variable `@b` and the tests for the controller and erb to both pass separately, but for the page loading to fail. We need system tests to make sure that our codebase is working properly.
In general, we don't write many controller tests because they tend to rely overly on mocking and are fully duplicitive with the system tests.
================================================
FILE: doc/architecture-decisions/0007-inline-css-for-email-views.md
================================================
# 1. Inline CSS for email views
Date: 2021-08-18
CSS is inline for email views becuase other forms of CSS lack full support in some email platforms including gmail and outlook.
See https://www.campaignmonitor.com/css/ to see the current status of support for forms of CSS such as `
Sorry, you are not authorized to perform this action.