Skip to content

Commit

Permalink
Clean up VSRs spreadsheet formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminJohnson2204 committed Apr 18, 2024
1 parent e2dbc01 commit 20fcb1e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 42 deletions.
152 changes: 113 additions & 39 deletions backend/src/controllers/vsr.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { RequestHandler } from "express";
import { validationResult } from "express-validator";
import createHttpError from "http-errors";
import VSRModel from "src/models/vsr";
import FurnitureItemModel, { FurnitureItem } from "src/models/furnitureItem";
import VSRModel, { FurnitureInput, VSR } from "src/models/vsr";
import {
sendVSRConfirmationEmailToVeteran,
sendVSRNotificationEmailToStaff,
} from "src/services/emails";
import validationErrorParser from "src/util/validationErrorParser";
import ExcelJS from "exceljs";
import { ObjectId } from "mongodb";

/**
* Gets all VSRs in the database. Requires the user to be signed in and have
Expand Down Expand Up @@ -158,11 +160,56 @@ export const deleteVSR: RequestHandler = async (req, res, next) => {
}
};

/**
* Converts an entry in a VSR to a formatted string to write to the Excel spreadsheet
*/
const stringifyEntry = (
entry: string | number | string[] | number[] | boolean | Date | undefined,
) => {
if (!entry) {
return "";
}

if (Array.isArray(entry)) {
return entry.join(", ");
} else {
return entry.toString();
}
};

type FurnitureItemEntry = FurnitureItem & { _id: ObjectId };

/**
* Formats a VSR's selected furniture items as a string
*/
const stringifySelectedFurnitureItems = (
selectedItems: FurnitureInput[] | undefined,
allFurnitureItems: FurnitureItemEntry[],
) => {
if (!selectedItems) {
return "";
}

const itemIdsToItems: Record<string, FurnitureItem> = {};

for (const furnitureItem of allFurnitureItems) {
itemIdsToItems[furnitureItem._id.toString()] = furnitureItem;
}

return selectedItems
.map((selectedItem) => {
const furnitureItem = itemIdsToItems[selectedItem.furnitureItemId];
return furnitureItem ? `${furnitureItem.name}: ${selectedItem.quantity}` : "[unknown]";
})
.join(", ");
};

const writeSpreadsheet = async (filename: string) => {
const workbook = new ExcelJS.Workbook();

workbook.creator = "PAP Inventory System";
workbook.lastModifiedBy = "Bot";

//current date
workbook.created = new Date();
workbook.modified = new Date();
Expand All @@ -173,49 +220,76 @@ const writeSpreadsheet = async (filename: string) => {
const vsrs = await VSRModel.find();
const plainVsrs = vsrs.map((doc) => doc.toObject());

// Setup columns headers based on keys from the first object in the plainVsrs array
if (plainVsrs.length > 0) {
worksheet.columns = Object.keys(plainVsrs[0]).map((key) => ({
header: key,
key: key,
width: 20,
}));

// Add data rows to the worksheet
plainVsrs.forEach((item) => {
worksheet.addRow(item);

//cast each element in the row to a string

//for the row that was just added in the spreadsheet, if there are brackets surrounding any of the entries, remove them
if (worksheet.lastRow) {
worksheet.lastRow.eachCell((cell) => {
if (cell.value && !(typeof cell.value === "number")) {
cell.value = cell.value.toString();
}
});

worksheet.lastRow.eachCell((cell) => {
if (
cell.value &&
typeof cell.value == "string" &&
cell.value[0] === "[" &&
cell.value[cell.value.length - 1] === "]"
) {
cell.value = cell.value.slice(1, -1);
}
});
}
});
// Fields that we want to write to the spreadsheet. First is field name, second is display name.
const fieldsToWrite: [keyof VSR, string][] = [
["name", "Name"],
["gender", "Gender"],
["age", "Age"],
["maritalStatus", "Marital Status"],
["spouseName", "Spouse Name"],
["agesOfBoys", "Ages of boys"],
["agesOfGirls", "Ages of girls"],
["ethnicity", "Ethnicity"],
["employmentStatus", "Employment Status"],
["incomeLevel", "Income Level"],
["sizeOfHome", "Size of Home"],

["streetAddress", "Street Address"],
["city", "City"],
["state", "State"],
["zipCode", "Zip Code"],
["phoneNumber", "Phone Number"],
["email", "Email Address"],
["branch", "Branch"],
["conflicts", "Conflicts"],
["dischargeStatus", "Discharge Status"],
["serviceConnected", "Service Connected"],
["lastRank", "Last Rank"],
["militaryID", "Military ID"],
["petCompanion", "Pet Companion Desired"],
["hearFrom", "Referral Source"],

["selectedFurnitureItems", "Selected Furniture Items"],
["additionalItems", "Additional Items"],

["dateReceived", "Date Received"],
["lastUpdated", "Last Updated"],
["status", "Status"],
];

worksheet.columns = fieldsToWrite.map((field) => ({
header: field[1],
key: field[0],
width: 20,
}));

const allFurnitureItems = await FurnitureItemModel.find();

// Add data rows to the worksheet
plainVsrs.forEach((vsr) => {
worksheet.addRow(
fieldsToWrite.reduce(
(prev, field) => ({
...prev,
[field[0]]:
field[0] === "selectedFurnitureItems"
? stringifySelectedFurnitureItems(
vsr[field[0]] as FurnitureInput[] | undefined,
allFurnitureItems,
)
: stringifyEntry(vsr[field[0]]),
}),
{},
),
);
});

// Write to file
await workbook.xlsx.writeFile(filename);
}
// Write to file
await workbook.xlsx.writeFile(filename);
};

export const bulkExportVSRS: RequestHandler = async (req, res, next) => {
try {
console.log("inside bulk export vsrs");
const filename = "vsrs.xlsx";
await writeSpreadsheet(filename);
res.download(filename);
Expand Down
2 changes: 1 addition & 1 deletion backend/src/models/furnitureItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ const furnitureItemSchema = new Schema({
categoryIndex: { type: Number, required: true },
});

type FurnitureItem = InferSchemaType<typeof furnitureItemSchema>;
export type FurnitureItem = InferSchemaType<typeof furnitureItemSchema>;

export default model<FurnitureItem>("FurnitureItem", furnitureItemSchema);
5 changes: 3 additions & 2 deletions backend/src/models/vsr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { InferSchemaType, Schema, model } from "mongoose";
/**
* A schema for a single furniture item that a veteran can request
*/
const furntitureInputSchema = new Schema({
export const furntitureInputSchema = new Schema({
// ID of the furniture item being required (Object ID for an instance of the furniture item model)
furnitureItemId: { type: String, required: true },

Expand Down Expand Up @@ -55,6 +55,7 @@ const vsrSchema = new Schema({
status: { type: String, required: true },
});

type VSR = InferSchemaType<typeof vsrSchema>;
export type FurnitureInput = InferSchemaType<typeof furntitureInputSchema>;
export type VSR = InferSchemaType<typeof vsrSchema>;

export default model<VSR>("VSR", vsrSchema);
Binary file removed backend/vsrs.xlsx
Binary file not shown.

0 comments on commit 20fcb1e

Please sign in to comment.