Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#5579): add topics to reports #5601

Merged
merged 9 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified app/documents/templates/default_report_template.docx
100755 → 100644
Binary file not shown.
Binary file modified app/documents/templates/howard_county_report_template.docx
Binary file not shown.
Binary file modified app/documents/templates/montgomery_report_template.docx
100755 → 100644
Binary file not shown.
Binary file modified app/documents/templates/prince_george_report_template.docx
100755 → 100644
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this one get smaller? Seems strange and maybe not good
Screenshot 2024-05-14 at 8 50 24 AM

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No clue. The template got longer so that's weird.
old: image

new: image

Binary file not shown.
48 changes: 47 additions & 1 deletion app/models/case_court_report_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def context
latest_hearing_date: latest_hearing_date,
org_address: org_address(@path_to_template),
volunteer: volunteer_info,
hearing_type_name: @court_date&.hearing_type&.name || "None"
hearing_type_name: @court_date&.hearing_type&.name || "None",
case_topics: court_topics.values
}
end

Expand Down Expand Up @@ -88,6 +89,51 @@ def org_address(path_to_template)
@volunteer.casa_org.address if @volunteer && is_default_template
end

# Sample output
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good comment :)

#
# expected_topics = {
# "Question 1" => {topic: "Question 1", details: "Details 1", answers: [
# {date: "12/02/20", medium: "Type A1, Type B1", value: "Answer 1"},
# {date: "12/03/20", medium: "Type A2, Type B2", value: "Answer 3"}
# ]},
# "Question 2" => {topic: "Question 2", details: "Details 2", answers: [
# {date: "12/02/20", medium: "Type A1, Type B1", value: "Answer 2"},
# {date: "12/04/20", medium: "Type A3, Type B3", value: "Answer 5"}
# ]},
# "Question 3" => {topic: "Question 3", details: "Details 3", answers: [
# {date: "12/03/20", medium: "Type A2, Type B2", value: "No Answer Provided"},
# {date: "12/04/20", medium: "Type A3, Type B3", value: "No Answer Provided"}
# ]}
# }
def court_topics
topics = ContactTopic
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice query. It could be slow but we are working with small numbers I think we're fine

.joins(contact_topic_answers: {case_contact: [:casa_case, :contact_types]}).distinct
.where("casa_cases.id": @casa_case.id)
.where("case_contacts.occurred_at": @date_range)
.order(:occurred_at, :value)
.select(:details, :question, :occurred_at, :value, :contact_made,
"STRING_AGG(contact_types.name, ', ' ORDER BY contact_types.name) AS medium_types")
.group(:details, :question, :occurred_at, :value, :contact_made)

topics.each_with_object({}) do |topic, hash|
hash[topic.question] ||= {
answers: [],
topic: topic.question,
details: topic.details
}

formatted_date = CourtReportFormatContactDate.new(topic).format_long
answer_value = topic.value.blank? ? "No Answer Provided" : topic.value
answer = {
date: formatted_date,
medium: topic.medium_types,
value: answer_value
}

hash[topic.question][:answers].push(answer)
end
end

private

def calculate_date_range(args)
Expand Down
4 changes: 4 additions & 0 deletions app/services/court_report_format_contact_date.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ def initialize(case_contact)
def format
I18n.l(@case_contact.occurred_at, format: :short_date, default: nil).concat(@case_contact.contact_made ? "" : CONTACT_UNSUCCESSFUL_PREFIX)
end

def format_long
I18n.l(@case_contact.occurred_at, format: :long_date, default: nil)
end
end
2 changes: 2 additions & 0 deletions app/views/case_contacts/form/details.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
</div>
</div>

<% if @case_contact.contact_topic_answers.any? %>
<div class="card-style-1 pl-25 mb-10">
<h4 class="mb-20 details__topics-label">Court report <%= "topic".pluralize(@case_contact.contact_topic_answers.count) %> <span class="content-1">(optional)</span></label></h4>
<div class="">
Expand All @@ -90,6 +91,7 @@
<% end %>
</div>
</div>
<% end %>
<div class="actions mb-10 d-flex justify-content-between">
<%= link_to leave_case_contacts_form_path, class: "btn-sm main-btn #{@case_contact.draft_case_ids.empty? ? 'danger' : 'primary'}-btn-outline btn-hover", data: { controller: "alert", "action": "alert#confirm", "alert-ignore-value": !@case_contact.draft_case_ids.empty?, "alert-title-value": "Discard draft?", "alert-message-value": "Are you sure? If you don't save and continue to the next step, this draft will not be recoverable." } do %>
Back
Expand Down
5 changes: 3 additions & 2 deletions app/views/case_contacts/form/expenses.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@
<% end %>
<% end %>
</ol>

<button type="button" class="btn-sm main-btn primary-btn-outline btn-hover" id="add-another-expense">+ Add Another Expense</button>
<% if @case_contact.decorate.additional_expenses_count < 10 %>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good improvement limit 10

<button type="button" class="btn-sm main-btn primary-btn-outline btn-hover" id="add-another-expense">+ Add Another Expense</button>
<% end %>
</div>
</div>
<% end %>
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ en:
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:
Expand Down
29 changes: 29 additions & 0 deletions lib/tasks/deployment/20240420230126_update_org_templates.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace :after_party do
desc "Deployment task: Updates_production_casa_orgs_with_new_templates"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scary but good

task update_org_templates: :environment do
puts "Running deploy task 'update_org_templates'"

mapping = {
"Howard County CASA" => "howard_county_report_template.docx",
"Voices for Children Montgomery" => "montgomery_report_template.docx",
"Prince George CASA" => "prince_george_report_template.docx"
}

mapping.each do |casa_org_name, template_file_name|
casa_org = CasaOrg.find_by(name: casa_org_name)
if casa_org
casa_org.court_report_template.attach(
io: File.new(Rails.root.join("app", "documents", "templates", template_file_name)),
filename: template_file_name
)
else
Bugsnag.notify("No #{casa_org_name} found for rake task update_org_templates")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good notify!

end
end

# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end
80 changes: 79 additions & 1 deletion spec/models/case_court_report_context_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
allow(context).to receive(:org_address).and_return(nil)
allow(context).to receive(:volunteer_info).and_return({})
allow(context).to receive(:latest_hearing_date).and_return("")
allow(context).to receive(:court_topics).and_return({})

expected_shape = {
created_date: "January 1, 2021",
Expand All @@ -36,7 +37,8 @@
latest_hearing_date: "",
org_address: nil,
volunteer: {},
hearing_type_name: court_date.hearing_type.name
hearing_type_name: court_date.hearing_type.name,
case_topics: []
}

expect(context.context).to eq(expected_shape)
Expand Down Expand Up @@ -176,6 +178,82 @@
end
end

describe "#court_topics" do
let(:org) { create(:casa_org) }
let(:casa_case) { create(:casa_case, casa_org: org) }
let(:topics) { [1, 2, 3].map { |i| create(:contact_topic, casa_org: org, question: "Question #{i}", details: "Details #{i}") } }
let(:contacts) do
[1, 2, 3, 4].map do |i|
create(:case_contact,
casa_case: casa_case,
occurred_at: 1.month.ago + i.days,
contact_types: [
create(:contact_type, name: "Type A#{i}"),
create(:contact_type, name: "Type B#{i}")
])
end
end
# let(:contacts) { create_list(:case_contact, 4, casa_case: casa_case, occurred_at: 1.month.ago) }

context "when given data" do
# Add some values that should get filtered out
before do
contact_one = create(:case_contact, casa_case: casa_case, medium_type: "in-person", occurred_at: 1.day.ago)
create_list(:contact_topic_answer, 2, case_contact: contact_one, contact_topic: topics[0], value: "Not included")

contact_two = create(:case_contact, casa_case: casa_case, medium_type: "in-person", occurred_at: 50.day.ago)
create_list(:contact_topic_answer, 2, case_contact: contact_two, contact_topic: topics[0], value: "Not included")

other_case = create(:casa_case, casa_org: org)
contact_three = create(:case_contact, casa_case: other_case, medium_type: "in-person", occurred_at: 50.day.ago)
create_list(:contact_topic_answer, 2, case_contact: contact_three, contact_topic: topics[0], value: "Not included")
end

it "generates correctly shaped data" do
# Contact 1 Answers
create(:contact_topic_answer, case_contact: contacts[0], contact_topic: topics[0], value: "Answer 1")
create(:contact_topic_answer, case_contact: contacts[0], contact_topic: topics[1], value: "Answer 2")

# Contact 2 Answers
create(:contact_topic_answer, case_contact: contacts[1], contact_topic: topics[0], value: "Answer 3")
create(:contact_topic_answer, case_contact: contacts[1], contact_topic: topics[2], value: nil)

# Contact 3 Answers
create(:contact_topic_answer, case_contact: contacts[2], contact_topic: topics[1], value: "Answer 5")
create(:contact_topic_answer, case_contact: contacts[2], contact_topic: topics[2], value: "")

# Contact 4 Answers
# No Answers

expected_topics = {
"Question 1" => {topic: "Question 1", details: "Details 1", answers: [
{date: "12/02/20", medium: "Type A1, Type B1", value: "Answer 1"},
{date: "12/03/20", medium: "Type A2, Type B2", value: "Answer 3"}
]},
"Question 2" => {topic: "Question 2", details: "Details 2", answers: [
{date: "12/02/20", medium: "Type A1, Type B1", value: "Answer 2"},
{date: "12/04/20", medium: "Type A3, Type B3", value: "Answer 5"}
]},
"Question 3" => {topic: "Question 3", details: "Details 3", answers: [
{date: "12/03/20", medium: "Type A2, Type B2", value: "No Answer Provided"},
{date: "12/04/20", medium: "Type A3, Type B3", value: "No Answer Provided"}
]}
}

court_report_context = build(:case_court_report_context, start_date: 45.day.ago.to_s, end_date: 5.day.ago.to_s, casa_case: casa_case)

expect(court_report_context.court_topics).to eq(expected_topics)
end
end

context "when there are no contact topics" do
it "returns an empty hash" do
court_report_context = build(:case_court_report_context, start_date: 45.day.ago.to_s, end_date: 5.day.ago.to_s, casa_case: casa_case)
expect(court_report_context.court_topics).to eq({})
end
end
end

describe "#filtered_interviewees" do
it "filters based on date range" do
casa_case = create(:casa_case)
Expand Down
113 changes: 113 additions & 0 deletions spec/models/case_court_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,114 @@
let(:path_to_report) { Rails.root.join("tmp", "test_report.docx").to_s }

context "#generate_to_string" do
let(:full_context) do
{
created_date: "April 9, 2024",
casa_case: {
court_date: "April 23, 2024",
case_number: "A-CASA-CASE-NUMBER-12345",
dob: "April 2012",
is_transitioning: false,
judge_name: "Judge Judy"
},
case_contacts: [
{name: "Some Name", type: "Type 1", dates: "4/09*", dates_by_medium_type: {"in-person" => "4/09*"}},
{name: "Some Other Name", type: "Type 4", dates: "4/09*", dates_by_medium_type: {"in-person" => "4/09*"}}
],
case_court_orders: [
{order: "case_court_order_text", status: "Partially implemented"}
],
case_mandates: [
{order: "case_mandates_text", status: "Partially implemented"}
],
latest_hearing_date: "___<LATEST HEARING DATE>____",
org_address: "596 Unique Avenue Seattle, Washington",
volunteer: {
name: "name_of_volunteer",
supervisor_name: "name_of_supervisor",
assignment_date: "February 9, 2024"
},
hearing_type_name: "None",
case_topics: [
{topic: "Question 1", details: "Details 1", answers: [
{date: "12/01/20", medium: "Type A1, Type B1", value: "Answer 1"},
{date: "12/02/20", medium: "Type A2, Type B2", value: "Answer 3"}
]},
{topic: "Question 2", details: "Details 2", answers: [
{date: "12/01/20", medium: "Type A1, Type B1", value: "Answer 2"},
{date: "12/02/20", medium: "Type A3, Type B3", value: "Answer 5"}
]},
{topic: "Question 3", details: "Details 3", answers: [
{date: "12/01/20", medium: "Type A3, Type B3", value: "No Answer Provided"},
{date: "12/02/20", medium: "Type A2, Type B2", value: "No Answer Provided"}
]}
]
}
end
describe "contact_topics" do
it "all contact topics are present in the report" do
docx_response = generate_doc(full_context, path_to_template)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Question 1.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Question 2.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Question 3.*/)
end

it "all topic details are present in the report" do
docx_response = generate_doc(full_context, path_to_template)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Details 1.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Details 2.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Details 3.*/)
end

it "all answers are present with correct format" do
docx_response = generate_doc(full_context, path_to_template)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Type A1, Type B1 \(12\/01\/20\): Answer 1.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Type A2, Type B2 \(12\/02\/20\): Answer 3.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Type A1, Type B1 \(12\/01\/20\): Answer 2.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Type A3, Type B3 \(12\/02\/20\): Answer 5.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Type A3, Type B3 \(12\/01\/20\): No Answer Provided.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Type A2, Type B2 \(12\/02\/20\): No Answer Provided.*/)
end

context "when there are topics but no answers" do
let(:curr_context) do
full_context[:case_topics] = [
{topic: "Question 1", details: "Details 1", answers: []},
{topic: "Question 2", details: "Details 2", answers: []},
{topic: "Question 3", details: "Details 3", answers: []}
]
end

it "all contact topics are present in the report" do
docx_response = generate_doc(full_context, path_to_template)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Question 1.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Question 2.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Question 3.*/)
end
it "all topic details are present in the report" do
docx_response = generate_doc(full_context, path_to_template)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Details 1.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Details 2.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Details 3.*/)
end
end

context "when there no topics" do
it "report does not error and puts old defaults" do
full_context[:case_topics] = []
docx_response = nil
expect {
docx_response = generate_doc(full_context, path_to_template)
}.not_to raise_error

expect(docx_response).not_to be_nil
expect(docx_response.paragraphs.map(&:to_s)).to include(/Placement.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Education\/Vocation.*/)
expect(docx_response.paragraphs.map(&:to_s)).to include(/Objective Information.*/)
end
end
end

describe "when receiving valid case, volunteer, and path_to_template" do
let(:volunteer) { create(:volunteer, :with_cases_and_contacts, :with_assigned_supervisor) }
let(:casa_case_with_contacts) { volunteer.casa_cases.first }
Expand Down Expand Up @@ -367,3 +475,8 @@
end
end
end

def generate_doc(context, path_to_template)
report = CaseCourtReport.new(path_to_template: path_to_template, context: context)
Docx::Document.open(StringIO.new(report.generate_to_string))
end
9 changes: 9 additions & 0 deletions spec/requests/case_contacts/form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@
end
end

context "when an org has no topics" do
let(:organization) { create(:casa_org) }
let!(:case_contact) { create(:case_contact, :details_status, casa_case: casa_case) }

it "does not show contact topic card" do
page = request.parsed_body.to_html
expect(page).to_not include("Court report topics")
end
end
context "when the org has topics assigned" do
let(:contact_topics) {
[
Expand Down
1 change: 1 addition & 0 deletions spec/system/case_contacts/additional_expenses_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@

expect(page).to have_no_field("case_contact_additional_expenses_attributes_10_other_expense_amount")
expect(page).to have_no_field("case_contact_additional_expenses_attributes_10_other_expenses_describe")
expect(casa_case.case_contacts.last.additional_expenses.count).to eq(10)
expect(page).to have_no_text("Add Another Expense")
end

Expand Down
Loading