Skip to content

Commit

Permalink
added docs for most bulk-delivery-related scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
leifwalsh committed Jul 27, 2020
1 parent 04b680a commit 3adbe33
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 25 deletions.
2 changes: 1 addition & 1 deletion functions/airtable.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ class ReconciledOrder {
/**
* Reconcile bulk delivery orders with procured items from Bulk Order table.
* @param {Date} deliveryDate Date these orders will go out.
* @param {[string, Object, Object][] | undefined} allRoutes All bulk delivery routes for this week.
* @param {[string, Object, Object][]} [allRoutes] All bulk delivery routes for this week.
* @returns {Promise<ReconciledOrder[]>} List of reconciled orders.
*/
async function reconcileOrders(deliveryDate, allRoutes) {
Expand Down
7 changes: 6 additions & 1 deletion functions/scripts/check-bulk-order.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ const {
} = require('../airtable');

async function main() {
const usageText = 'Usage: $0 --delivery-date YYYY-MM-DD'
+ '\n\nCheck the current bulk order against the set of Bulk Delivery Confirmed tickets.'
+ '\n\nThis script reads the current order from the Bulk Order table, and all Intake Tickets marked with the status Bulk Delivery Confirmed, and prints the difference between what we have ordered and what was requested.'
+ '\n\nPositive numbers indicate a surplus, negative numbers indicate additional items we need Shopping Volunteers to procure, in addition to Custom Items.';
const { argv } = yargs
.option('deliveryDate', {
coerce: (x) => new Date(x),
demandOption: true,
describe: 'Date of scheduled delivery (yyyy-mm-dd format)',
});
})
.usage(usageText);

// Note: we compare our bulk order against all tickets with this status,
// rather than finding tickets based on what's in a bulk delivery route,
Expand Down
23 changes: 19 additions & 4 deletions functions/scripts/email-bulk-delivery-volunteers.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,31 @@ function getEmailTemplateParameters(route, tickets) {
}

async function main() {
const usageText = 'Usage: $0 --delivery-date YYYY-MM-DD [OPTIONS]'
+ '\n\nSends an email to delivery volunteers registered to deliver on the specified delivery date, with instructions about delivery day, and the tickets assigned to them with information they will need.'
+ '\n\nIf you need to email just one volunteer, use --route to select one route number for that date.'
+ '\n\nWith --dry-run, the email content will be printed to the console.'
+ '\n\nTo include yourself in the Bcc list (to make sure the emails sent properly), pass --bcc.'
+ '\n\nPreconditions:'
+ '\n\n This script reads the Bulk Delivery Routes table for the specified date, and looks up Delivery Volunteers assigned to those routes, and the Intake Tickets attached to those routes, so check those tables for correctness before running this.';
const { argv } = yargs
.option('deliveryDate', {
coerce: (x) => new Date(x),
demandOption: true,
describe: 'Date of scheduled delivery (yyyy-mm-dd format)',
})
.option('route', {
coerce: String,
demandOption: false,
describe: 'Email just one delivery volunteer for a specific route ID',
describe: 'Send email for a specific route ID',
type: 'string',
})
.boolean('dryRun');
.option('bcc', {
demandOption: false,
describe: 'Add Bcc recipient(s) to all emails (comma separated)',
type: 'string',
})
.boolean('dryRun')
.usage(usageText);

const routes = argv.route ? (
await getRecordsWithFilter(BULK_DELIVERY_ROUTES_TABLE, { deliveryDate: argv.deliveryDate, name: argv.route })
Expand All @@ -66,18 +78,21 @@ async function main() {
return new Email(markdown, {
to: view.to,
cc: '[email protected]',
bcc: _.map(_.split(argv.bcc || '', ','), (address) => _.trim(address)),
replyTo: '[email protected]',
subject: `[BSS Bulk Ordering] ${view.deliveryDateString} Delivery Prep and Instructions for ${view.firstName}`,
});
});

if (argv.dryRun) {
console.log(emails);
_.forEach(emails, (email) => console.log(email.render()));
} else {
await Promise.all(_.map(emails, (email) => {
return email.send();
}));
}

console.log('********************************************************************************\n\nNEXT STEPS!\n\nYou sent the delivery volunteers their coordination emails, they are all set to deliver! Great job!\n\n********************************************************************************');
}

main().then(
Expand Down
29 changes: 22 additions & 7 deletions functions/scripts/email-bulk-shopping-volunteers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,29 @@ const { reconcileOrders, getAllRoutes } = require('../airtable');
const { Email, googleMapsUrl } = require('../messages');

async function main() {
const usageText = 'Usage: $0 --delivery-date YYYY-MM-DD [OPTIONS]'
+ '\n\nSends an email to shopping volunteers registered to do custom shopping on the specified delivery date, with instructions about delivery day, and shopping lists for the tickets assigned to them.'
+ '\n\nWith --dry-run, the email content will be printed to the console.'
+ '\n\nTo include yourself in the Bcc list (to make sure the emails sent properly), pass --bcc.'
+ '\n\nPreconditions:'
+ '\n\n This script reads the Bulk Delivery Routes table for the specified date, and looks up Shopping Volunteers assigned to those routes, and the Intake Tickets attached to those routes, so check those tables for correctness before running this.'
+ '\n\n This script also reads the Bulk Order table to compare it with the total groceries requested in tickets scheduled for bulk delivery, to determine what extra items we did not procure, which shoppers need to fulfill, so check to make sure that table has been updated to reflect the actually procured bulk items before running this.'
+ '\n\n You should run this together with generate-packing-slips.js, so that the shopping lists for shopping volunteers accurately match the items in the Other category on the packing slips.'
+ '\n\n You should probably run this in --dry-run mode, and generate the packing slips, and check at least a few tickets to make sure that the shopping lists match the Other category on the packing slips, before sending the packing slips to Brooklyn Packers or the shopping lists to our Shopping Volunteers.'
+ '\n\n Finally, check with the Bulk Delivery Coordinator for this week to find out if there are any special items this week---either bulk items we need Shopping Volunteers to fill in the gaps on, or custom items we can actually provide in a bulk-like way that are not in the Bulk Order table. Check the source code for this script, there is a "CUSTOMIZATION" section you may need to edit in that case.';
const { argv } = yargs
.option('deliveryDate', {
coerce: (x) => new Date(x),
demandOption: true,
describe: 'Date of scheduled delivery (yyyy-mm-dd format)',
})
.boolean('dryRun');
.option('bcc', {
demandOption: false,
describe: 'Add Bcc recipient(s) to all emails (comma separated)',
type: 'string',
})
.boolean('dryRun')
.usage(usageText);

// --------------------------------------------------------------------------
// CUSTOMIZATION
Expand Down Expand Up @@ -117,6 +133,7 @@ async function main() {
return new Email(markdown, {
to: view.to,
cc: '[email protected]',
bcc: _.map(_.split(argv.bcc || '', ','), (address) => _.trim(address)),
replyTo: '[email protected]',
subject: `[BSS Bulk Ordering] ${view.deliveryDateString} Delivery Prep and Instructions for ${view.firstName}`,
});
Expand All @@ -125,13 +142,11 @@ async function main() {
if (argv.dryRun) {
_.forEach(emails, (email) => console.log(email.render()));
} else {
await Promise.all(
_.map(emails, (email) => {
return email.send();
})
);
await Promise.all(_.map(emails, (email) => {
return email.send();
}));
}
return null;
console.log('********************************************************************************\n\nNEXT STEPS!\n\nYou sent the shopping volunteers their coordination emails, they are all set to go shopping! Great job!\n\n********************************************************************************');
}

main()
Expand Down
24 changes: 20 additions & 4 deletions functions/scripts/generate-order-sheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ const {
} = require('../airtable');

async function main() {
const usageText = 'Usage: $0 <mode> --delivery-date YYYY-MM-DD [OPTIONS]'
+ '\n\nThis script fills the Bulk Order table for the specified delivery date. After running this, we need to extract the Bulk Order table into a spreadsheet and send it to Brooklyn Packers; if you do not know how, the Bulk Delivery Coordinator will help you.'
+ '\n\nThere are two modes: predict and finalize.'
+ '\n\n In predict mode, we consider all historical tickets given certain criteria, and aggregate them to come up with a likely bulk order based on the number of households we are planning to do bulk ordering and delivery to in a given week. We give this order to Brooklyn Packers as early in the week as we can, so they can start sourcing.'
+ '\n\n In finalize mode, we look at the Intake Tickets with the Bulk Delivery Confirmed status, sum all the items requested, and set that as our final bulk order for that week. We do this after confirming delivery windows for all candidate bulk delivery tickets, to give Brooklyn Packers our final order for the week, ideally, with as much time as possible for them to finish sourcing.'
+ '\n\nIn both cases, we pad the order by a percentage, to account for orders added late in the week, changes in requirements, and to just have a little extra available at the warehouse for people that walk by and ask if they can have something (which is awesome).'
+ '\n\nThe configurable factors in prediction mode are the --max-household-size we plan to deliver to this week (we consider only historical tickets under this size), and the --num-households we plan to deliver to this week (this is a scaling factor for the final order). Run generate-order-sheet.js predict --help to see these options.'
+ '\n\nThe order padding is also configurable, the default is 15% but can be changed with --buffer-ratio.'
+ '\n\nWhen run, this script will overwrite all rows in the Bulk Order table for the specified delivery date, so if something goes wrong, you can always run it again after fixing what went wrong. It will not modify rows in that table for a different delivery date.'
+ '\n\nYou can run with --dry-run to print the bulk order to the console, in this case no records will be deleted or written.'
+ '\n\nPreconditions:'
+ '\n\n In "predict" mode, there are basically no preconditions, it just reads a few weeks of historical Intake Tickets data.'
+ '\n\n In "finalize" mode, it bases the order on Intake Tickets with the status Bulk Delivery Confirmed, so you should verify that the Intake Volunteers are finished calling back bulk delivery candidates and have finalized the confirmed list for this week.';
const { argv } = yargs
.command('predict', 'Predict an upcoming order based on past tickets', {
'max-household-size': {
Expand All @@ -39,14 +52,15 @@ async function main() {
type: 'number'
})
.boolean('dry-run')
.demandCommand(1, 1, 'Please provide a command', 'Only one command at a time');
.demandCommand(1, 1, 'Please provide a command', 'Only one command at a time')
.usage(usageText);

console.log('Generating the order sheet...');

const itemToNumRequested = argv._[0] === 'predict' ? (
await predictOrder(argv)
await predictOrder(_.pick(argv, ['numHouseholds', 'maxHouseholdSize']))
) : (argv._[0] === 'finalize' ? (
await finalizeOrder(argv)
await finalizeOrder()
) : (() => { throw new Error(`Unknown command ${argv._}`); })());

const paddedItemToNumRequested = padOrder(itemToNumRequested, argv.bufferRatio);
Expand All @@ -58,6 +72,8 @@ async function main() {
} else {
await populateBulkOrder(bulkOrderRows, argv.deliveryDate);
}

console.log('********************************************************************************\n\nNEXT STEPS!\n\nThe bulk order has been updated in https://airtable.com/tblTHuZevdWqikl8G/viwgdKMpRdh6omsZO\n\nNow, make sure the Bulk Delivery Coordinator gets this forwarded to Brooklyn Packers!\n\n********************************************************************************');
}

const predictOrder = async ({ numHouseholds, maxHouseholdSize }) => {
Expand Down Expand Up @@ -164,7 +180,7 @@ const populateBulkOrder = async (bulkOrderRows, deliveryDate) => {
return fields.deliveryDate === moment(deliveryDate).utc().format('YYYY-MM-DD');
}
);
await Promise.all(_.map(oldBulkOrderRecords, async ([id,,]) => { await deleteRecord(BULK_ORDER_TABLE, id); }));
await Promise.all(_.map(oldBulkOrderRecords, async ([id, ,]) => { await deleteRecord(BULK_ORDER_TABLE, id); }));

// Add the new bulk order
await Promise.all(_.map(bulkOrderRows, async (row) => { await createRecord(BULK_ORDER_TABLE, row); }));
Expand Down
23 changes: 15 additions & 8 deletions functions/scripts/generate-packing-slips.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,25 @@ async function savePackingSlips(orders) {
}

async function main() {
const { argv } = yargs.option('deliveryDate', {
coerce: (x) => new Date(x),
demandOption: true,
describe: 'Date of scheduled delivery (yyyy-mm-dd format)',
});
const usageText = 'Usage: $0 --delivery-date YYYY-MM-DD'
+ '\n\nGenerates packing slips as a PDF in the out/ directory. This needs to be sent to Brooklyn Packers prior to delivery day, so they can label the boxes to know what to pack in each one.'
+ '\n\nPreconditions:'
+ '\n\n This script reads the Bulk Delivery Routes table for the specified date, and looks up the Intake Tickets attached to those routes, so check those tables for correctness before running this.'
+ '\n\n This script also reads the Bulk Order table to compare it with the total groceries requested in tickets scheduled for bulk delivery, to determine what extra items we did not procure, which will go in the Other section, so check to make sure that table has been updated to reflect the actually procured bulk items before running this.'
+ '\n\n You should run this together with email-bulk-shopping-volunteers.js in --dry-run mode, so that the shopping lists for shopping volunteers accurately match the items in the Other category on the packing slips.'
+ '\n\n You should probably run this to generate the packing slips, and run email-bulk-shopping-volunteers.js in --dry-run mode, and check at least a few tickets to make sure that the shopping lists match the Other category on the packing slips, before sending the packing slips to Brooklyn Packers or the shopping lists to our Shopping Volunteers.';
const { argv } = yargs
.option('deliveryDate', {
coerce: (x) => new Date(x),
demandOption: true,
describe: 'Date of scheduled delivery (yyyy-mm-dd format)',
})
.usage(usageText);

const orders = await reconcileOrders(argv.deliveryDate);

console.log('Creating packing slips...');

const outPath = await savePackingSlips(orders);
console.log('Wrote packing slips to', outPath);
console.log(`********************************************************************************\n\nNEXT STEPS!\n\nPacking slips have been generated in ${outPath}.\n\nNow, make sure the Bulk Delivery Coordinator gets this forwarded to Brooklyn Packers!\n\n********************************************************************************`);
}

main()
Expand Down

0 comments on commit 3adbe33

Please sign in to comment.