Skip to content

Commit

Permalink
Merge branch 'main' into data-validation-script
Browse files Browse the repository at this point in the history
  • Loading branch information
rahearn authored Mar 15, 2021
2 parents faf295b + c3e2272 commit 524a88b
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 47 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/FileUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ const FileTable = ({ onFileRemoved, files }) => {
</td>
<td>
<Button
role="button"
type="button"
className="smart-hub--file-tag-button"
unstyled
aria-label="remove file"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ const ResourceSelector = ({ name, ariaName }) => {

const canDelete = fields.length > 1;

const onAddNewResource = () => {
const allValues = getValues();
const fieldArray = allValues[name] || [];
const canAdd = fieldArray.every((field) => field.value !== '');
if (canAdd) {
append({ value: '' });
}
};

return (
<>
{fields.map((item, index) => (
Expand All @@ -27,6 +36,11 @@ const ResourceSelector = ({ name, ariaName }) => {
type="text"
defaultValue={item.value}
inputRef={register()}
onKeyUp={(e) => {
if (e.key === 'Enter') {
onAddNewResource();
}
}}
/>
{canDelete && (
<Button onClick={() => remove(index)} aria-label={`remove ${ariaName} ${index + 1}`} className="smart-hub--remove-resource padding-left-2" unstyled type="button">
Expand All @@ -38,14 +52,7 @@ const ResourceSelector = ({ name, ariaName }) => {
<Button
unstyled
type="button"
onClick={() => {
const allValues = getValues();
const fieldArray = allValues[name] || [];
const canAdd = fieldArray.every((field) => field.value !== '');
if (canAdd) {
append({ value: '' });
}
}}
onClick={onAddNewResource}
>
<span className="fa-layers fa-fw">
<FontAwesomeIcon color="#0166ab" size="lg" icon={faCircle} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ describe('ResourceSelector', () => {
});
});

describe('when enter is pressed', () => {
it('adds a new resource', async () => {
render(<RenderResourceSelector data={[{ value: '' }]} />);
const textBox = await screen.findByTestId('textInput');
userEvent.type(textBox, 'test{enter}');
const text = await screen.findAllByRole('textbox');
expect(text.length).toBe(2);
});
});

describe('with multiple entries', () => {
it('allows removal of an item', async () => {
render(<RenderResourceSelector data={[{ value: 'first' }, { value: 'second' }]} />);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/ActivityReport/Pages/nextSteps.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const NoteEntries = ({ name, humanName }) => {
</div>
)
: (
<Button unstyled onClick={() => onEdit(notes.length)}>
<Button type="button" unstyled onClick={() => onEdit(notes.length)}>
<FontAwesomeIcon icon={faPlusCircle} />
<span className="padding-left-05">Add New Follow Up</span>
</Button>
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/pages/ActivityReport/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ function ActivityReport({
return array.map((value) => ({ value }));
};

const convertReportToFormData = (fetchedReport) => {
const ECLKCResourcesUsed = unflattenResourcesUsed(fetchedReport.ECLKCResourcesUsed);
const nonECLKCResourcesUsed = unflattenResourcesUsed(fetchedReport.nonECLKCResourcesUsed);
return { ...fetchedReport, ECLKCResourcesUsed, nonECLKCResourcesUsed };
};

useDeepCompareEffect(() => {
const fetch = async () => {
let report;
Expand All @@ -103,9 +109,7 @@ function ActivityReport({
updateLoading(true);
if (activityReportId !== 'new') {
const fetchedReport = await getReport(activityReportId);
const ECLKCResourcesUsed = unflattenResourcesUsed(fetchedReport.ECLKCResourcesUsed);
const nonECLKCResourcesUsed = unflattenResourcesUsed(fetchedReport.nonECLKCResourcesUsed);
report = { ...fetchedReport, ECLKCResourcesUsed, nonECLKCResourcesUsed };
report = convertReportToFormData(fetchedReport);
} else {
report = {
...defaultValues,
Expand Down Expand Up @@ -213,18 +217,21 @@ function ActivityReport({
};

const onFormSubmit = async (data) => {
const report = await submitReport(reportId.current, data);
const fetchedReport = await submitReport(reportId.current, data);
const report = convertReportToFormData(fetchedReport);
updateFormData(report);
updateEditable(false);
};

const onReview = async (data) => {
const report = await reviewReport(reportId.current, data);
const fetchedReport = await reviewReport(reportId.current, data);
const report = convertReportToFormData(fetchedReport);
updateFormData(report);
};

const onResetToDraft = async () => {
const report = await resetToDraft(reportId.current);
const fetchedReport = await resetToDraft(reportId.current);
const report = convertReportToFormData(fetchedReport);
updateFormData(report);
updateEditable(true);
};
Expand Down
9 changes: 9 additions & 0 deletions src/migrations/20210315181828-goal-timeframe-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.changeColumn('Goals', 'timeframe', { type: Sequelize.TEXT });
},

down: async (queryInterface, Sequelize) => {
await queryInterface.changeColumn('Goals', 'timeframe', { type: Sequelize.STRING });
},
};
18 changes: 11 additions & 7 deletions src/services/activityReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,18 +488,22 @@ export async function createOrUpdate(newActivityReport, report) {
author,
granteeNextSteps,
specialistNextSteps,
ECLKCResourcesUsed,
nonECLKCResourcesUsed,
...allFields
} = newActivityReport;

const ECLKCResourcesUsed = allFields.ECLKCResourcesUsed
? allFields.ECLKCResourcesUsed.map((item) => item.value)
: [];
const resources = {};

const nonECLKCResourcesUsed = allFields.nonECLKCResourcesUsed
? allFields.nonECLKCResourcesUsed.map((item) => item.value)
: [];
if (ECLKCResourcesUsed) {
resources.ECLKCResourcesUsed = ECLKCResourcesUsed.map((item) => item.value);
}

if (nonECLKCResourcesUsed) {
resources.nonECLKCResourcesUsed = nonECLKCResourcesUsed.map((item) => item.value);
}

const updatedFields = { ...allFields, ECLKCResourcesUsed, nonECLKCResourcesUsed };
const updatedFields = { ...allFields, ...resources };
await sequelize.transaction(async (transaction) => {
if (report) {
savedReport = await update(updatedFields, report, transaction);
Expand Down
8 changes: 5 additions & 3 deletions src/tools/importActivityReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import { REPORT_STATUSES } from '../constants';
const columnCleanupRE = /(\s?\(.*\)|:|\.|\/|&|')+/g;
const decimalRE = /^\d+(\.\d*)?$/;
const invalidRegionRE = /R14/;
const grantNumRE = /\|\s+(?<grantNumber>[0-9A-Z]+)\n/g;
const grantNumRE = /\|\s+(?<grantNumber>[0-9A-Z]+)(\n|$)/g;
const mdyDateRE = /^\d{1,2}\/\d{1,2}\/(\d{2}|\d{4})$/;
const mdyFormat = 'MM/DD/YYYY';

Expand Down Expand Up @@ -274,10 +274,12 @@ export default async function importActivityReports(fileKey, region) {
// Imported ARs won't pass `checkRequiredForSubmission`,
// because `approvingManagerId`, `requester`, etc. may be null
// so we build, then save without validating;
const [ar] = await ActivityReport.findOrBuild(
const [ar, built] = await ActivityReport.findOrBuild(
{ where: { legacyId }, defaults: arRecord },
);
ar.save({ validate: false });
if (built) {
await ar.save({ validate: false });
}

// ActivityRecipients: connect Grants to ActivityReports
const grantNumbers = parseGrantNumbers(getValue(data, 'granteeName'));
Expand Down
61 changes: 40 additions & 21 deletions src/tools/importPlanGoals.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ async function prePopulateRoles() {
updateOnDuplicate: ['updatedAt'],
});
}

const grantNumRE = /\s(?<grantNumber>[0-9]{2}[A-Z]{2}[0-9]+)(?:[,\s]|$)/g;
const parseGrantNumbers = (value) => {
const matchIter = value.matchAll(grantNumRE);
const results = [];
for (const { groups: { grantNumber } } of matchIter) {
if (grantNumber) {
results.push(grantNumber);
}
}
return results;
};

/**
* Processes data from .csv inserting the data during the processing as well as
* creating data arrays for associations and then inserting them to the database
Expand All @@ -66,32 +79,35 @@ export default async function importGoals(fileKey, region) {
const cleanRoleTopics = [];
const cleanGrantGoals = [];
const cleanTopicGoals = [];
const currentGoals = [];

await prePopulateRoles();

for await (const el of grantees) {
let currentGranteeId;
let grants;
let currentGrants = [];
let currentGoalName;
let currentGoal = {};
const currentGoals = [];
let currentGoalName = '';
let currentGoalNum = 0;

for await (const key of Object.keys(el)) {
if (key && (key.trim().startsWith('Grantee (distinct') || key.trim().startsWith('Grantee Name'))) {
grants = el[key] ? el[key].split('|')[1].trim() : 'Unknown Grant';
currentGrants = grants.split(',');
currentGrants = parseGrantNumbers(el[key]);
} else if (key && key.startsWith('Goal')) {
const goalColumn = key.split(' ');
let column;
if (goalColumn.length === 2) { // Column name is "Goal X" representing goal's name
currentGoalName = el[key].trim();
if (currentGoalName.match(/(no goals?|none)( identified)? at this time\.?/i)) {
currentGoalName = '';
}
if (currentGoalName !== '') { // Ignore empty goals
currentGoal = { name: currentGoalName }; // change to dbGoal
const goalNum = goalColumn[1];
currentGoals[goalNum] = { ...currentGoals[goalNum], ...currentGoal };
// eslint-disable-next-line prefer-destructuring
currentGoalNum = goalColumn[1];
currentGoals[currentGoalNum] = {
...currentGoals[currentGoalNum],
name: currentGoalName,
};
}
} else {
} else if (currentGoalName !== '') {
// column will be either "topics", "timeframe" or "status"
column = goalColumn[2].toLowerCase();
if (column === 'topics') {
Expand Down Expand Up @@ -124,16 +140,17 @@ export default async function importGoals(fileKey, region) {
}
// Add topic to junction with goal
cleanTopicGoals.push(
{ topicId, goalName: currentGoal.name },
{ topicId, goalName: currentGoalName },
); // we don't have goal's id at this point yet
}
}
} else // it's either "timeframe" or "status"
// both "timeframe" and "status" column names will be reused as goal's object keys
if (currentGoalName !== '') {
currentGoal[column] = el[key].trim();
const goalNum = goalColumn[1].slice(0, 1); // represents a goal number from 1 to 5
currentGoals[goalNum] = { ...currentGoals[goalNum], ...currentGoal };
} else {
// it's either "timeframe" or "status"
// both "timeframe" and "status" column names will be reused as goal's object keys
currentGoals[currentGoalNum] = {
...currentGoals[currentGoalNum],
[column]: el[key].trim(),
};
}
}
}
Expand All @@ -142,12 +159,14 @@ export default async function importGoals(fileKey, region) {
// after each row
let goalId;
let grantId;
let currentGranteeId;

for await (const goal of currentGoals) {
if (goal) { // ignore the dummy element at index 0
const [dbGoal] = await Goal.findOrCreate(
{ where: { ...goal, isFromSmartsheetTtaPlan: true } },
);
const [dbGoal] = await Goal.findOrCreate({
where: { name: goal.name, isFromSmartsheetTtaPlan: true },
defaults: goal,
});
goalId = dbGoal.id;
// add goal id to cleanTopicGoals
cleanTopicGoals.forEach((tp) => {
Expand Down

0 comments on commit 524a88b

Please sign in to comment.