diff --git a/app/models/exporters/base_exporter.rb b/app/models/exporters/base_exporter.rb index dabed24636..e6776de8b1 100644 --- a/app/models/exporters/base_exporter.rb +++ b/app/models/exporters/base_exporter.rb @@ -10,7 +10,7 @@ class Exporters::BaseExporter FIRST_ROW_INDEX = 1 attr_accessor :locale, :lookups, :fields, :field_names, :forms, :field_value_service, - :location_service, :record_type, :user, :options + :location_service, :record_type, :user, :options, :record_data_service class << self def supported_models @@ -49,13 +49,19 @@ def initialize(output_file_path = nil, config = {}, options = {}) establish_export_constraints end - def export(_records) - raise NotImplementedError + def export(records) + records.each { |record| embed_associated_data(record) } + end + + def embed_associated_data(record) + # If we need to embed other associated data we can add methods from the RecordDataService in this class. + record.data = record_data_service.embed_family_info(record.data, record, field_names) end def intialize_services self.location_service = LocationService.instance self.field_value_service = FieldValueService.new(location_service:) + self.record_data_service = RecordDataService.new end def establish_export_constraints diff --git a/app/models/exporters/csv_exporter.rb b/app/models/exporters/csv_exporter.rb index c29da0a7f1..5d58143a1d 100644 --- a/app/models/exporters/csv_exporter.rb +++ b/app/models/exporters/csv_exporter.rb @@ -24,6 +24,7 @@ def excluded_field_names end def export(records) + super(records) csv_export = CSVSafe.generate do |rows| rows << headers if @called_first_time.nil? @called_first_time ||= true @@ -42,8 +43,6 @@ def headers end def row(record, fields) - data = record.data - data['family_details_section'] = record.family_members_details if record.is_a?(Child) - [record.id] + fields.map { |field| data[field.name] } + [record.id] + fields.map { |field| record.data[field.name] } end end diff --git a/app/models/exporters/csv_list_view_exporter.rb b/app/models/exporters/csv_list_view_exporter.rb index 0d00a766d1..4e44907bf4 100644 --- a/app/models/exporters/csv_list_view_exporter.rb +++ b/app/models/exporters/csv_list_view_exporter.rb @@ -29,6 +29,7 @@ def setup_export_constraints? end def export(records) + super(records) csv_export = build_csv_export(records, list_headers) buffer.write(csv_export) end diff --git a/app/models/exporters/excel_exporter.rb b/app/models/exporters/excel_exporter.rb index 540af063f3..e797603fa5 100644 --- a/app/models/exporters/excel_exporter.rb +++ b/app/models/exporters/excel_exporter.rb @@ -27,6 +27,7 @@ def initialize(output_file_path = nil, config = {}, options = {}) end def export(records) + super(records) constraint_subforms build_worksheets_with_headers @@ -112,7 +113,6 @@ def format_form_name(name) def write_record(record) data = record.data - data['family_details_section'] = record.family_members_details if record.is_a?(Child) forms.each do |form| write_record_form(record.short_id, data, form, form&.subform_field&.name) diff --git a/app/models/exporters/incident_recorder_exporter.rb b/app/models/exporters/incident_recorder_exporter.rb index 50458ab128..e75569e9f1 100644 --- a/app/models/exporters/incident_recorder_exporter.rb +++ b/app/models/exporters/incident_recorder_exporter.rb @@ -37,6 +37,7 @@ def complete # @returns: a String with the Excel file data def export(models) + super(models) @builder.export(models) end diff --git a/app/models/exporters/json_exporter.rb b/app/models/exporters/json_exporter.rb index 9129b8a4fd..5c5336fc41 100644 --- a/app/models/exporters/json_exporter.rb +++ b/app/models/exporters/json_exporter.rb @@ -17,6 +17,7 @@ def supported_models end def export(records) + super(records) hashes = records.map { |m| convert_model_to_hash(m) } buffer.write(JSON.pretty_generate(hashes)) end @@ -26,7 +27,6 @@ def convert_model_to_hash(record) json_parse = JSON.parse(record.to_json) data_fields = json_parse['data'].select { |k, _| field_names.include?(k) } json_parse['data'] = data_fields - json_parse['data']['family_details_section'] = record.family_members_details if record.is_a?(Child) json_parse end end diff --git a/spec/models/exporters/csv_exporter_spec.rb b/spec/models/exporters/csv_exporter_spec.rb index 2fb20dbedb..86f49967ce 100644 --- a/spec/models/exporters/csv_exporter_spec.rb +++ b/spec/models/exporters/csv_exporter_spec.rb @@ -9,6 +9,9 @@ module Exporters family = Family.create!( data: { + family_number: 'FA-001', + family_size: 5, + family_notes: 'FamilyNotes', family_members: [ { unique_id: '001', relation_name: 'George', relation_age: 10, relation_sex: 'male' }, { unique_id: '002', relation_name: 'FirstName2 LastName2', relation_age: 12, relation_sex: 'female' } @@ -46,6 +49,9 @@ module Exporters form_group_id: 'case_form_1', order: 7, fields: [ + Field.new(name: 'family_number', display_name: 'Family Number', type: 'text_field', visible: true), + Field.new(name: 'family_size', display_name: 'Family Size', type: 'numeric_field', visible: true), + Field.new(name: 'family_notes', display_name: 'Family Notes', type: 'text_field', visible: true), Field.new( name: 'family_details_section', display_name_en: 'Family Details', @@ -87,6 +93,7 @@ module Exporters family:, data: { family_member_id: '001', + family_number: 'CA-001', name: 'George', age: 10, sex: 'male', @@ -100,25 +107,33 @@ module Exporters data = CsvExporter.export(@records, nil, { user: @user }) parsed = CSV.parse(data) - expect(parsed[0]).to eq %w[id name age sex family_details_section] + expect(parsed[0]).to eq %w[id name age sex family_number family_size family_notes family_details_section] expect(parsed[1][1..3]).to eq(%w[Joe 12 male]) expect(parsed[2][1..3]).to eq(%w[Mo 14 male]) expect(parsed[3][1..3]).to eq(%w[George 10 male]) end - it 'export the family_details_section' do + it 'exports the family_details_section' do data = CsvExporter.export(@records, nil, { user: @user }) parsed = CSV.parse(data) - expect(parsed[1][4]).to eq( + expect(parsed[1][7]).to eq( '[{"relation_name"=>"John", "relation"=>"father"}, {"relation_name"=>"Mary", "relation"=>"mother"}]' ) - expect(parsed[3][4]).to eq( + expect(parsed[3][7]).to eq( '[{"unique_id"=>"002", "relation"=>"relation2", "relation_name"=>"FirstName2 LastName2", ' \ '"relation_age"=>12, "relation_sex"=>"female"}]' ) end + it 'exports the global family fields from the family record' do + data = CsvExporter.export(@records, nil, { user: @user }) + parsed = CSV.parse(data) + + expect(parsed[0]).to eq %w[id name age sex family_number family_size family_notes family_details_section] + expect(parsed[3][4..6]).to eq(%w[FA-001 5 FamilyNotes]) + end + it 'sanitizes formula injections' do unsafe_record = Child.new(data: { name: 'Joe', age: 12, sex: '=10+10' }) data = CsvExporter.export([unsafe_record], nil, { user: @user }) diff --git a/spec/models/exporters/excel_exporter_spec.rb b/spec/models/exporters/excel_exporter_spec.rb index 72540fc1ae..e480a76521 100644 --- a/spec/models/exporters/excel_exporter_spec.rb +++ b/spec/models/exporters/excel_exporter_spec.rb @@ -138,6 +138,9 @@ module Exporters form_group_id: 'case_form_1', order: 7, fields: [ + Field.new(name: 'family_number', display_name: 'Family Number', type: 'text_field', visible: true), + Field.new(name: 'family_size', display_name: 'Family Size', type: 'numeric_field', visible: true), + Field.new(name: 'family_notes', display_name: 'Family Notes', type: 'text_field', visible: true), Field.new( name: 'family_details_section', display_name_en: 'Family Details', @@ -157,6 +160,9 @@ module Exporters @user = create(:user, user_name: 'fakeadmin', role: @role) @family = Family.create!( data: { + family_number: 'FA-001', + family_size: 5, + family_notes: 'FamilyNotes', family_members: [ { unique_id: '001', relation_name: 'FirstName1 LastName1', relation_age: 10, relation_sex: 'male' }, { unique_id: '002', relation_name: 'FirstName2 LastName2', relation_age: 12, relation_sex: 'female' } @@ -192,6 +198,9 @@ module Exporters family: @family, data: { family_member_id: '001', + family_number: 'CA-001', + family_size: 0, + family_notes: 'CaseNotes', first_name: 'FirstName1', last_name: 'LastName1', age: 10, @@ -210,13 +219,13 @@ module Exporters end it 'contains a worksheet for each form and subform' do - expect(workbook.sheets.size).to eq(9) + expect(workbook.sheets.size).to eq(10) expect(workbook.sheets).to match_array( [ 'cases_test_form_2', 'cases_test_form_1', 'Test Arabic فاكيا قد به،. بـ...', 'cases_test_form-cases_test_s...', 'cases_test_form-cases_test_s.-1', 'cases_test_form-cases_test_s.-2', 'cases_test_form-cases_test_s.-3', - 'case_test_form_-cases_test_s...', 'Family Details-Nested Family...' + 'case_test_form_-cases_test_s...', 'Family Details', 'Family Details-Nested Family...' ] ) end @@ -264,11 +273,18 @@ module Exporters expect(workbook.sheet(7).row(2)).to eq([@record_id, nil]) end - it 'exports the family details section' do + it 'exports the family global fields' do expect(workbook.sheet(8).last_row).to eq(3) - expect(workbook.sheet(8).row(1)).to eq(%w[ID Name Age Sex Relation]) - expect(workbook.sheet(8).row(2)).to eq([@records[0].short_id, 'Detail1', 5, 'male', 'relation1']) - expect(workbook.sheet(8).row(3)).to eq( + expect(workbook.sheet(8).row(1)).to eq(['ID', 'Family Number', 'Family Size', 'Family Notes']) + expect(workbook.sheet(8).row(2)).to eq([@record_id, nil, nil, nil]) + expect(workbook.sheet(8).row(3)).to eq([@records[1].short_id, 'FA-001', 5, 'FamilyNotes']) + end + + it 'exports the family details section' do + expect(workbook.sheet(9).last_row).to eq(3) + expect(workbook.sheet(9).row(1)).to eq(%w[ID Name Age Sex Relation]) + expect(workbook.sheet(9).row(2)).to eq([@records[0].short_id, 'Detail1', 5, 'male', 'relation1']) + expect(workbook.sheet(9).row(3)).to eq( [@records[1].short_id, 'FirstName2 LastName2', 12, 'female', 'relation2'] ) end diff --git a/spec/models/exporters/json_exporter_spec.rb b/spec/models/exporters/json_exporter_spec.rb index 3d9a6e9016..2c386f00bb 100644 --- a/spec/models/exporters/json_exporter_spec.rb +++ b/spec/models/exporters/json_exporter_spec.rb @@ -8,6 +8,9 @@ family = Family.create!( data: { + family_number: 'FA-001', + family_size: 5, + family_notes: 'FamilyNotes', family_members: [ { unique_id: '001', relation_name: 'FirstName1 LastName1', relation_age: 10, relation_sex: 'male' }, { unique_id: '002', relation_name: 'FirstName2 LastName2', relation_age: 12, relation_sex: 'female' } @@ -51,6 +54,9 @@ form_group_id: 'case_form_1', order: 7, fields: [ + Field.new(name: 'family_number', display_name: 'Family Number', type: 'text_field', visible: true), + Field.new(name: 'family_size', display_name: 'Family Size', type: 'numeric_field', visible: true), + Field.new(name: 'family_notes', display_name: 'Family Notes', type: 'text_field', visible: true), Field.new( name: 'family_details_section', display_name_en: 'Family Details', @@ -89,6 +95,9 @@ family:, data: { family_member_id: '001', + family_number: 'CA-001', + family_size: 0, + family_notes: 'CaseNotes', first_name: 'FirstName1', last_name: 'LastName1', age: 10, @@ -120,6 +129,10 @@ end it 'handles family linked data' do + expect(data_hash2[0]['data']['family_number']).to eq('FA-001') + expect(data_hash2[0]['data']['family_size']).to eq(5) + expect(data_hash2[0]['data']['family_notes']).to eq('FamilyNotes') + expect(data_hash2[0]['data']['family_details_section'].size).to eq(1) expect(data_hash2[0]['data']['family_details_section'].size).to eq(1) expect(data_hash2[0]['data']['family_details_section'][0]['relation_name']).to eq('FirstName2 LastName2') expect(data_hash2[0]['data']['family_details_section'][0]['relation']).to eq('relation2')