Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/CMS-Enterprise/eAPD into dm…
Browse files Browse the repository at this point in the history
…irano/4551-ffp-screen-reader-bug
  • Loading branch information
mirano-darren committed Mar 30, 2023
2 parents 597400d + fd80377 commit 35c3f8e
Show file tree
Hide file tree
Showing 37 changed files with 1,728 additions and 181 deletions.
46 changes: 2 additions & 44 deletions api/db/apds.js
@@ -1,55 +1,13 @@
import fastPatch from 'fast-json-patch';
import {
deepCopy,
calculateBudget,
hasBudgetUpdate,
APD_TYPE
} from '@cms-eapd/common';
import { deepCopy, calculateBudget, hasBudgetUpdate } from '@cms-eapd/common';
import loggerFactory from '../logger/index.js';
import { updateStateProfile } from './states.js';
import adminCheckApd from '../util/adminCheck.js';
import knex from './knex.js';
import {
Budget,
HITECHBudget,
MMISBudget,
APD,
HITECH,
MMIS
} from '../models/index.js';
import { Budget, APD, getApdModel, getBudgetModel } from '../models/index.js';

const logger = loggerFactory('db/apds');

const getApdModel = apdType => {
let model;
switch (apdType) {
case APD_TYPE.HITECH:
model = HITECH;
break;
case APD_TYPE.MMIS:
model = MMIS;
break;
default:
model = APD;
}
return model;
};

const getBudgetModel = apdType => {
let model;
switch (apdType) {
case APD_TYPE.HITECH:
model = HITECHBudget;
break;
case APD_TYPE.MMIS:
model = MMISBudget;
break;
default:
model = Budget;
}
return model;
};

export const createAPD = async apd => {
const apdJSON = deepCopy(apd);
const { apdType } = apd;
Expand Down
168 changes: 85 additions & 83 deletions api/migrations/20230207224139_mongoose-adding-funding-category.js
Expand Up @@ -3,6 +3,7 @@ import { setup, teardown } from '../db/mongodb.js';
import { APD, Budget } from '../models/index.js';
import {
APD_TYPE,
BUDGET_TYPE,
FUNDING_CATEGORY_TYPE,
calculateBudget,
deepCopy
Expand Down Expand Up @@ -30,115 +31,116 @@ const updateFundingCategory = ({ federal = null, state = null } = {}) => {
export const up = async () => {
// Grab all APDs
await setup();
const apds = await APD.find({
$or: [
{
'activities.costAllocation.$[].ffp.fundingCategory': { $exists: false }
},
{
'keyStatePersonnel.keyPersonnel.split.$[].fundingCategory': {
$exists: false
}
}
]
}).lean();
// { virtuals: true } returns apdType along with __t
const apds = await APD.find().lean({ virtuals: true });
logger.info(`Updating ${apds.length} APDs`);

const apdIds = [];
const budgetIds = [];
const updatedApds = [];

const updatedApds = apds.map(apd => {
apdIds.push(apd._id);
if (apd.budget) {
budgetIds.push(apd.budget);
}
apds.forEach(apd => {
const apdJSON = deepCopy(apd);
const years = apd.years;
const apdType = apd.__t;

apdJSON.keyStatePersonnel.keyPersonnel =
apdJSON.keyStatePersonnel.keyPersonnel.map(keyPersonnel => ({
...keyPersonnel,
split: years.reduce(
/**
* Iterate over each year and return an object with the year as the key and
* set the default for existing HITECH APDs to a 90/10 split.
* If years is ['2022', '2023'] the resulting object will look like this:
* {
* '2022': { federal: 90, state: 10 }
* '2023': { federal: 90, state: 10 }
* }
*/
(acc, year) => ({
...acc,
[year]: {
...keyPersonnel?.split?.[year],
fundingCategory: updateFundingCategory(
keyPersonnel?.split?.[year]
)
}
}),
{}
)
}));
let updated = false;

apdJSON.activities = apdJSON.activities.map(activity => ({
...activity,
costAllocation: years.reduce((acc, year) => {
if (
activity?.costAllocation?.[year]?.ffp?.fundingCategory !==
undefined &&
activity?.costAllocation?.[year]?.ffp?.fundingCategory !== null
) {
apdJSON.keyStatePersonnel.keyPersonnel =
apdJSON.keyStatePersonnel.keyPersonnel.map(keyPersonnel => {
if (keyPersonnel?.split?.[years[0]]?.fundingCategory === undefined) {
updated = true;
return {
...acc,
[year]: {
ffp: {
...activity?.costAllocation?.[year]?.ffp
}
}
...keyPersonnel,
split: years.reduce(
/**
* Iterate over each year and return an object with the year as the key and
* set the default for existing HITECH APDs to a 90/10 split.
* If years is ['2022', '2023'] the resulting object will look like this:
* {
* '2022': { federal: 90, state: 10 }
* '2023': { federal: 90, state: 10 }
* }
*/
(acc, year) => ({
...acc,
[year]: {
...keyPersonnel?.split?.[year],
fundingCategory: updateFundingCategory(
keyPersonnel?.split?.[year]
)
}
}),
{}
)
};
}
return keyPersonnel;
});

apdJSON.activities = apdJSON.activities.map(activity => {
if (
activity?.costAllocation?.[years[0]]?.ffp?.fundingCategory === undefined
) {
updated = true;
return {
...acc,
[year]: {
ffp: {
federal:
apdType === APD_TYPE.HITECH
? activity?.costAllocation?.[year]?.ffp?.federal
: 0,
state:
apdType === APD_TYPE.HITECH
? activity?.costAllocation?.[year]?.ffp?.state
: 0,
fundingCategory: null
},
other:
apdType === APD_TYPE.HITECH
? activity?.costAllocation?.[year]?.other
: 0
}
...activity,
costAllocation: years.reduce(
(acc, year) => ({
...acc,
[year]: {
ffp: {
federal:
apdType === APD_TYPE.HITECH
? activity?.costAllocation?.[year]?.ffp?.federal
: 0,
state:
apdType === APD_TYPE.HITECH
? activity?.costAllocation?.[year]?.ffp?.state
: 0,
fundingCategory: null
},
other:
apdType === APD_TYPE.HITECH
? activity?.costAllocation?.[year]?.other
: 0
}
}),
{}
)
};
}, {})
}));
}
return activity;
});

return apdJSON;
if (updated) {
logger.info(`APD ${apdJSON._id} needs to be updated`);
updatedApds.push(apdJSON);
}
});

if (updatedApds.length === apds.length) {
if (updatedApds.length > 0) {
await Promise.all(
updatedApds.map(apd => {
logger.info(`Updating APD ${apd._id} to add funding category`);
return APD.replaceOne({ _id: apd._id }, { ...apd });
return APD.replaceOne({ _id: apd._id }, { ...apd, __t: apd.__t });
})
).catch(err => logger.error(err));
await Promise.all(
updatedApds.map(apd => {
logger.info(`Updating APD ${apd._id} Budget ${apd.budget}`);
const budget = calculateBudget(apd);
return Budget.replaceOne({ _id: apd.budget }, { ...budget });

const apdType = apd.__t;
const budget = calculateBudget({
...apd,
apdType
});
return Budget.replaceOne(
{ _id: apd.budget },
{ ...budget, __t: BUDGET_TYPE[apdType] }
);
})
).catch(err => logger.error(err));
} else {
logger.info('Nothing to update');
}
await teardown();
};
Expand Down
@@ -1,7 +1,7 @@
import loggerFactory from '../logger/index.js';
import { setup, teardown } from '../db/mongodb.js';
import { MMIS, MMISBudget } from '../models/index.js';
import { calculateBudget } from '@cms-eapd/common';
import { MMIS, Budget } from '../models/index.js';
import { BUDGET_TYPE, calculateBudget } from '@cms-eapd/common';

const logger = loggerFactory('mongoose-migrate/mmis-budget-match-rate');

Expand All @@ -22,7 +22,10 @@ export const up = async () => {
const budget = calculateBudget(apd);

try {
return MMISBudget.replaceOne({ _id: apd.budget }, { ...budget });
return Budget.replaceOne(
{ _id: apd.budget },
{ ...budget, __t: BUDGET_TYPE.MMIS_BUDGET }
);
} catch (err) {
logger.error(err);
}
Expand Down
74 changes: 74 additions & 0 deletions api/migrations/20230327181241_mongoose-update-budgets.js
@@ -0,0 +1,74 @@
import loggerFactory from '../logger/index.js';
import { setup, teardown } from '../db/mongodb.js';
import { APD, Budget, HITECH, MMIS } from '../models/index.js';
import { BUDGET_TYPE, calculateBudget } from '@cms-eapd/common';

const logger = loggerFactory('mongoose-migrate/updating-budgets');

/**
* Update the existing database to match the new model
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export const up = async () => {
// Grab all APDs
await setup();

// Update HITECH Budgets
// { virtuals: true } returns apdType along with __t
const hitech = await HITECH.find().lean({ virtuals: true });
logger.info(`Update ${hitech.length} HITECH Budgets`);

const hitechResults = await Promise.allSettled(
hitech.map(apd => {
logger.info(`Updating APD ${apd._id} Budget ${apd.budget}`);

const budget = calculateBudget({ ...apd });
return Budget.replaceOne(
{ _id: apd.budget },
{ ...budget, __t: BUDGET_TYPE.HITECH_BUDGET },
{
multipleCastError: true,
runValidators: false,
upsert: true
}
);
})
);
logger.info(`HITECH results: ${JSON.stringify(hitechResults)}`);

// Update MMIS Budgets
// { virtuals: true } returns apdType along with __t
const mmis = await MMIS.find().lean({ virtuals: true });
logger.info(`Update ${mmis.length} MMIS Budgets`);

const mmisResults = await Promise.allSettled(
mmis.map(apd => {
logger.info(`Updating APD ${apd._id} Budget ${apd.budget}`);

const budget = calculateBudget({ ...apd });
return Budget.replaceOne(
{ _id: apd.budget },
{ ...budget, __t: BUDGET_TYPE.MMIS_BUDGET },
{
multipleCastError: true,
runValidators: false,
upsert: true
}
);
})
);
logger.info(`MMIS results: ${JSON.stringify(mmisResults)}`);

const apds = await APD.find({ __t: null }).lean();
logger.info(`Found ${apds.length} plain APDs`);

await teardown();
};

/**
* Make any changes that UNDO the up function side effects here (if possible)
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export const down = async () => {};
3 changes: 2 additions & 1 deletion api/models/hitech.js
Expand Up @@ -6,6 +6,7 @@ import {
discriminatorOptions,
federalCitation
} from './apd.js';
import { APD_TYPE } from '@cms-eapd/common';

const incentivePayment = new mongoose.Schema(
{
Expand Down Expand Up @@ -131,6 +132,6 @@ const hitechSchema = new mongoose.Schema(
discriminatorOptions
);

const HITECH = APD.discriminator('HITECH', hitechSchema);
const HITECH = APD.discriminator(APD_TYPE.HITECH, hitechSchema);

export default HITECH;
6 changes: 5 additions & 1 deletion api/models/hitechBudget.js
Expand Up @@ -9,6 +9,7 @@ import {
fedStateSplit,
activities
} from './budget.js';
import { BUDGET_TYPE } from '@cms-eapd/common';

const hitechBudgetSchema = new mongoose.Schema(
{
Expand Down Expand Up @@ -70,6 +71,9 @@ const hitechBudgetSchema = new mongoose.Schema(
discriminatorOptions
);

const HITECHBudget = Budget.discriminator('HITECHBudget', hitechBudgetSchema);
const HITECHBudget = Budget.discriminator(
BUDGET_TYPE.HITECH_BUDGET,
hitechBudgetSchema
);

export default HITECHBudget;

0 comments on commit 35c3f8e

Please sign in to comment.