Skip to content

Commit

Permalink
feat: add contact topics to reports
Browse files Browse the repository at this point in the history
In #5388 I added ContacTopics and ContactTopicAnswers to CaseContacts.
This allowed CasaOrgs to set topics and Users to create
ContactTopicAnswers on specific CaseContacts.

Now we want to have those answers appear in the court reports.

This PR modifies CourtReportContext to include topic information, as
well as modifies CourtReport templates to accept that information.
  • Loading branch information
elasticspoon committed Apr 10, 2024
1 parent b449a47 commit deea6bb
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 2 deletions.
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/prince_george_report_template.docx
100755 → 100644
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
#
# 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
.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
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
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
82 changes: 82 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,83 @@
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 no answers" do
it "report does not error" do
full_context[:case_topics] = []
expect { generate_doc(full_context, path_to_template) }.not_to raise_error
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 +444,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

0 comments on commit deea6bb

Please sign in to comment.