Skip to content

Commit

Permalink
feat: add category of funding activity as search field (#2267)
Browse files Browse the repository at this point in the history
Also updated labels on several existing grant and search fields.
  • Loading branch information
sanason authored Dec 13, 2023
1 parent 7d3758b commit 8323e28
Show file tree
Hide file tree
Showing 16 changed files with 235 additions and 26 deletions.
4 changes: 4 additions & 0 deletions packages/client/src/components/Modals/GrantDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
<div v-for="field in dialogFields" :key="field">
<p><span class="data-label">{{ titleize(field) }}:</span> {{ selectedGrant[field] }}</p>
</div>
<p>
<span class="data-label">Category of Funding Activity:</span>
{{ selectedGrant['funding_activity_categories']?.join(', ') }}
</p>
<p class="data-label">Description:</p>
<div style="max-height: 170px; overflow-y: scroll">
<div style="white-space: pre-line" v-html="selectedGrant.description"></div>
Expand Down
22 changes: 19 additions & 3 deletions packages/client/src/components/Modals/SearchPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
</b-form-group>
<b-form-group
id="opportunity-category-group"
label="Category"
label="Opportunity Category"
label-for="opportunity-category"
>
<v-select
Expand Down Expand Up @@ -156,6 +156,20 @@
:clearable="false"
/>
</b-form-group>
<b-form-group
id="category-of-funding-activity-group"
label="Category of Funding Activity"
label-for="category-of-funding-activity"
>
<v-select
id="category-of-funding-activity"
v-model="formData.criteria.fundingActivityCategories"
:options="fundingActivityCategories"
:multiple="true"
:close-on-select="false"
label="name"
/>
</b-form-group>
<b-form-group
id="agency-group"
label="Agency Code"
Expand Down Expand Up @@ -234,6 +248,7 @@ const defaultCriteria = {
bill: null,
costSharing: null,
opportunityCategories: [],
fundingActivityCategories: [],
postedWithin: [],
};
Expand Down Expand Up @@ -295,6 +310,7 @@ export default {
computed: {
...mapGetters({
eligibilityCodes: 'grants/eligibilityCodes',
fundingActivityCategories: 'grants/fundingActivityCategories',
savedSearches: 'grants/savedSearches',
displaySearchPanel: 'grants/displaySearchPanel',
}),
Expand All @@ -320,13 +336,13 @@ export default {
updateSavedSearch: 'grants/updateSavedSearch',
fetchSavedSearches: 'grants/fetchSavedSearches',
applyFilters: 'grants/applyFilters',
fetchEligibilityCodes: 'grants/fetchEligibilityCodes',
fetchSearchConfig: 'grants/fetchSearchConfig',
changeSelectedSearchId: 'grants/changeSelectedSearchId',
initNewSearch: 'grants/initNewSearch',
initViewResults: 'grants/initViewResults',
}),
setup() {
this.fetchEligibilityCodes();
this.fetchSearchConfig();
if (this.displaySearchPanel) {
this.showSideBar();
}
Expand Down
7 changes: 5 additions & 2 deletions packages/client/src/helpers/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ const FILTER_FIELD_NAME_MAP = {
includeKeywords: 'Include',
excludeKeywords: 'Exclude',
opportunityNumber: 'Opportunity Number',
opportunityStatuses: 'Opportunity Statuses',
opportunityStatuses: 'Opportunity Status',
fundingTypes: 'Funding Type',
agency: 'Agency Code',
costSharing: 'Cost Sharing',
opportunityCategories: 'Opportunity Categories',
opportunityCategories: 'Opportunity Category',
reviewStatus: 'Review Status',
postedWithin: 'Posted Within',
eligibility: 'Eligibility',
bill: 'Bill',
fundingActivityCategories: 'Category of Funding Activity',
};

export function formatFilterDisplay(criteria) {
Expand All @@ -35,6 +36,8 @@ export function formatFilterDisplay(criteria) {
newVal = value.map((i) => i.label).join(' or ');
} else if (key === 'fundingTypes') {
newVal = value.map((i) => i.name).join(' or ');
} else if (key === 'fundingActivityCategories') {
newVal = value.map((i) => i.name).join(' or ');
}

filters.push({
Expand Down
12 changes: 12 additions & 0 deletions packages/client/src/store/modules/grants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function initialState() {
return {
grantsPaginated: {},
eligibilityCodes: [],
fundingActivityCategories: [],
interestedCodes: [],
grantsInterested: [],
closestGrants: [],
Expand Down Expand Up @@ -53,6 +54,7 @@ function buildGrantsNextQuery({ filters, ordering, pagination }) {
postedWithin
reviewStatus
bill
fundingActivityCategories
*/
const criteria = { ...filters };
// Validate and fix the inputs into appropriate types.
Expand All @@ -61,6 +63,7 @@ function buildGrantsNextQuery({ filters, ordering, pagination }) {
criteria.eligibility = criteria.eligibility?.map((e) => e.code);
criteria.fundingTypes = criteria.fundingTypes?.map((f) => f.code);
criteria.bill = criteria.bill === 'All Bills' ? null : criteria.bill;
criteria.fundingActivityCategories = criteria.fundingActivityCategories?.map((c) => c.code);

if (!criteria.opportunityStatuses || criteria.opportunityStatuses.length === 0) {
// by default, only show posted opportunities
Expand Down Expand Up @@ -99,6 +102,7 @@ export default {
totalInterestedGrants: (state) => state.totalInterestedGrants,
currentGrant: (state) => state.currentGrant,
eligibilityCodes: (state) => state.eligibilityCodes,
fundingActivityCategories: (state) => state.fundingActivityCategories,
interestedCodes: (state) => ({
rejections: state.interestedCodes.filter((c) => c.status_code === 'Rejected'),
result: state.interestedCodes.filter((c) => c.status_code === 'Result'),
Expand Down Expand Up @@ -206,6 +210,10 @@ export default {
fetchApi.get('/api/organizations/:organizationId/eligibility-codes')
.then((data) => commit('SET_ELIGIBILITY_CODES', data));
},
fetchSearchConfig({ commit }) {
fetchApi.get('/api/organizations/:organizationId/search-config')
.then((data) => commit('SET_SEARCH_CONFIG', data));
},
fetchInterestedCodes({ commit }) {
fetchApi.get('/api/organizations/:organizationId/interested-codes')
.then((data) => commit('SET_INTERESTED_CODES', data));
Expand Down Expand Up @@ -300,6 +308,10 @@ export default {
SET_ELIGIBILITY_CODES(state, eligibilityCodes) {
state.eligibilityCodes = eligibilityCodes;
},
SET_SEARCH_CONFIG(state, searchConfig) {
state.eligibilityCodes = searchConfig.eligibilityCodes;
state.fundingActivityCategories = searchConfig.fundingActivityCategories;
},
SET_INTERESTED_CODES(state, interestedCodes) {
state.interestedCodes = interestedCodes;
},
Expand Down
2 changes: 2 additions & 0 deletions packages/server/__tests__/api/grants.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ describe('`/api/grants` endpoint', () => {
'Appropriations Bill',
'Agency Code',
'Eligibility',
'Category of Funding Activity',
];

const txt = await response.text();
Expand All @@ -505,6 +506,7 @@ describe('`/api/grants` endpoint', () => {
expect(valMap.get('Funding Type')).to.equal('Other');
expect(valMap.get('Agency Code')).to.equal('HHS-IHS');
expect(valMap.get('Eligibility')).to.equal('"Native American tribal organizations (other than Federally recognized tribal governments)|Others(see text field entitled ""Additional Information on Eligibility"" for clarification)|Native American tribal governments(Federally recognized)"');
expect(valMap.get('Category of Funding Activity')).to.equal('Health|Income Security and Social Services');
});

it('produces same number of rows as grid', async () => {
Expand Down
27 changes: 27 additions & 0 deletions packages/server/__tests__/db/db.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,12 +406,27 @@ describe('db', () => {
const result = await db.getSingleGrantDetails({ grantId, tenantId: fixtures.users.staffUser.tenant_id });
expect(result.interested_agencies.length).to.equal(1);
});
it('gets the viewed by agencies', async () => {
const grantId = '335255';
const result = await db.getSingleGrantDetails({ grantId, tenantId: fixtures.users.staffUser.tenant_id });
expect(result.viewed_by_agencies.length).to.equal(1);
});
it('maps funding activity category codes to funding activity category names', async () => {
const grantId = '335255';
const result = await db.getSingleGrantDetails({ grantId, tenantId: fixtures.users.staffUser.tenant_id });
expect(result.funding_activity_categories).to.deep.equal(['Income Security and Social Services']);
});
it('returns dates in string format without timezone', async () => {
const grantId = '335255';
const result = await db.getSingleGrantDetails({ grantId, tenantId: fixtures.users.staffUser.tenant_id });
expect(result.open_date).to.equal('2021-08-11');
expect(result.close_date).to.equal('2021-11-03');
});
it('handles grant not found', async () => {
const grantId = '435255';
const result = await db.getSingleGrantDetails({ grantId, tenantId: fixtures.users.staffUser.tenant_id });
expect(result).to.be.null;
});
});

context('getGrantsAssignedAgency', () => {
Expand Down Expand Up @@ -710,6 +725,18 @@ describe('db', () => {
expect(result.data[4].award_ceiling).to.be.null;
expect(result.data[5].award_ceiling).to.be.null;
});
it('gets grants that have any of the funding activity category codes', async () => {
const result = await db.getGrantsNew(
{ fundingActivityCategories: ['IS', 'ST'] },
{ currentPage: 1, perPage: 10, isLengthAware: true },
{ orderBy: 'open_date', orderDesc: 'true' },
fixtures.tenants.SBA.id,
fixtures.agencies.accountancy.id,
);
expect(result).to.have.property('data').with.lengthOf(2);
expect(result.data[0].grant_id).to.equal(fixtures.grants.redefiningPossible.grant_id);
expect(result.data[1].grant_id).to.equal(fixtures.grants.healthAide.grant_id);
});
});

context('getAgency', () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/server/__tests__/db/seeds/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ const grants = {
raw_body: 'raw body',
funding_instrument_codes: 'CA G PC',
bill: 'Infrastructure Investment and Jobs Act (IIJA)',
funding_activity_category_codes: 'ISS',
created_at: '2021-08-11 11:30:38.89828-07',
updated_at: '2021-08-11 12:30:39.531-07',
},
Expand All @@ -147,6 +148,7 @@ const grants = {
raw_body: 'raw body',
funding_instrument_codes: 'O',
bill: '',
funding_activity_category_codes: 'IS',
created_at: '2021-08-06 16:03:53.57025-07',
updated_at: '2021-08-11 12:35:42.562-07',
},
Expand All @@ -171,6 +173,7 @@ const grants = {
raw_body: 'raw body',
funding_instrument_codes: 'O PC',
bill: 'Infrastructure Investment and Jobs Act (IIJA)',
funding_activity_category_codes: 'ISS ST',
created_at: '2021-01-06 11:30:38.89828-07',
updated_at: '2022-04-23 12:30:39.531-07',
},
Expand Down Expand Up @@ -363,6 +366,16 @@ const grantsSavedSearches = [
},
];

const grantsViewed = {
entry1: {
agency_id: agencies.accountancy.id,
grant_id: grants.earFellowship.grant_id,
user_id: users.adminUser.id,
created_at: '2022-08-06 16:03:53.57025-07',
updated_at: '2021-08-11 12:35:42.562-07',
},
};

module.exports = {
tenants,
agencies,
Expand All @@ -375,6 +388,7 @@ module.exports = {
grants,
interestedCodes,
roles,
grantsViewed,
};

module.exports.seed = async (knex) => {
Expand Down Expand Up @@ -402,4 +416,5 @@ module.exports.seed = async (knex) => {
await knex(TABLES.assigned_grants_agency).insert(Object.values(assignedAgencyGrants));
await knex(TABLES.grants_interested).insert(Object.values(grantsInterested));
await knex(TABLES.grants_saved_searches).insert(Object.values(grantsSavedSearches));
await knex(TABLES.grants_viewed).insert(Object.values(grantsViewed));
};
13 changes: 13 additions & 0 deletions packages/server/__tests__/lib/grants-ingest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ describe('processMessages', async () => {
{ code: '13' }, { code: '12' },
{ code: '11' }, { code: '10' },
],
funding_activity: {
categories: [
{
name: 'Humanities',
code: 'HU',
},
{
name: 'Business and Commerce',
code: 'BC',
},
],
},
revision: { id: 'c3' },
}),
ReceiptHandle: 'receipt-handle-1',
Expand Down Expand Up @@ -194,6 +206,7 @@ describe('processMessages', async () => {
description: 'Here is a description of this superb grant',
eligibility_codes: '25 20 13 12 11 10',
opportunity_status: 'archived',
funding_activity_category_codes: 'HU BC',
raw_body: sinon.match(
jsonMatcher(JSON.stringify(JSON.parse(messages[2].Body).detail.versions.new)),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.table('grants', (table) => {
table.text('funding_activity_category_codes');
});
};

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.table('grants', (table) => {
table.dropColumn('funding_activity_category_codes');
});
};
Loading

0 comments on commit 8323e28

Please sign in to comment.