-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add reminder emails for collection system
- Loading branch information
Showing
12 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Fill these in to send emails | ||
DOCSOC_SMTP_SERVER=smtp-mail.outlook.com | ||
DOCSOC_SMTP_PORT=587 | ||
DOCSOC_OUTLOOK_USERNAME= | ||
# Password to docsoc email | ||
DOCSOC_OUTLOOK_PASSWORD= | ||
|
||
# Optional: Fill these in to uplod drafts | ||
# You will need to create an app registration in Entra ID, restricted to the organisation, | ||
# And grant it the following permissions: | ||
# - Mail.ReadWrite | ||
# - User.Read | ||
MS_ENTRA_CLIENT_ID= | ||
MS_ENTRA_CLIENT_SECRET= | ||
MS_ENTRA_TENANT_ID= | ||
|
||
# DB URL for use by getUncollectedPeople | ||
COLLECTION_DATABASE_URL= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
attachments/* | ||
!attachments/.gitkeep | ||
|
||
data/* | ||
!data/.gitkeep | ||
!data/example.csv | ||
|
||
output/* | ||
!output/.gitkeep |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
To get started: | ||
|
||
1. Put your own CSV in the `data` folder, with at the minimum `to` and `subject` columns. | ||
1. You can also add `cc` and `bcc` columns (to use them you will need to pass the correct CLI option though) | ||
2. `to`, `cc`, and `bcc` can be a space-separated list of emails. | ||
3. You can add any other columns you like, and they will be available in the template. | ||
4. For attachments, add a column with the name `attachment` with a singular path to the file to attach relative to th workspace root (e.g. `./attachments/image1.jpg`). | ||
1. Or, pass the same attachment to every email using the `-a` flag to `generate` | ||
5. For multiple attachments, have separate columns e.g. `attachment1`, `attachment2`, etc. | ||
6. See `data/example.csv` for an example. | ||
2. Put your own nunjucks markdown email template in the `templates` folder. | ||
1. You can also edit the default `wrapper.html.njk` file - this is what the markdown HTML will be wrapped in when sending it. It muat _always_ include a `{{ content }}` tag, which will be replaced with the markdown HTML. | ||
3. Fill in the `.env` file with your email credentials. | ||
|
||
Then run the following commands: | ||
|
||
```bash | ||
docsoc-mailmerge generate nunjucks ./data/my-data.csv ./templates/my-template.md.njk -o ./output --htmlTemplate ./templates/wrapper.html.njk | ||
# make some edits to the outputs and regenerate them: | ||
docsoc-mailmerge regenerate ./output/<runname> | ||
# review them, then send: | ||
docsoc-mailmerge send ./output/<runname> | ||
``` | ||
|
||
The CLI tool has many options - use `--help` to see them all: | ||
|
||
```bash | ||
docsoc-mailmerge generate nunjucks --help | ||
docsoc-mailmerge regenerate --help | ||
docsoc-mailmerge send --help | ||
``` | ||
|
||
## What happen when you generate | ||
|
||
1. Each record in the CSV will result in 3 files in `./output/<runname>`: an editable markdown file to allow you to modify the email, a HTML rendering of the markdown that you should not edit, and a `.json` metadata file | ||
2. The HTML files, which is what is actually sent, can be regenerated after edting the markdown files with `regenerate` command (see below) | ||
3. If you want to edit the to address or subject after this point you will need to edit the JSON files; csv edits are ignored. If you edit the CSV, delete all outputs and run generate again. | ||
|
||
## If the .env file is missing | ||
|
||
Use this template: | ||
|
||
```bash | ||
# Fill these in to send emails | ||
DOCSOC_SMTP_SERVER=smtp-mail.outlook.com | ||
DOCSOC_SMTP_PORT=587 | ||
DOCSOC_OUTLOOK_USERNAME= | ||
# Password to docsoc email | ||
DOCSOC_OUTLOOK_PASSWORD= | ||
|
||
# Optional: Fill these in to uplod drafts | ||
# You will need to create an app registration in Entra ID, restricted to the organisation, | ||
# And grant it the following permissions: | ||
# - Mail.ReadWrite | ||
# - User.Read | ||
MS_ENTRA_CLIENT_ID= | ||
MS_ENTRA_CLIENT_SECRET= | ||
MS_ENTRA_TENANT_ID= | ||
``` |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
name,to,subject,attachment1,attachment2,bcc,cc | ||
Example Person,email1@example.com email2@example.co.uk,Example subject,./attachments/image1.jpg,./attachments/image2.jpg,bcc@london.com,cc@must-cc.org otherperson@example.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/** | ||
* Outputs a JSON file with the students who have not collected their merch, for use by the mailmerge tool | ||
* to send a reminder to these people. | ||
* | ||
* Created with co-pilot. | ||
*/ | ||
import { PrismaClient } from "@prisma/client"; | ||
// Load .env file ".env" | ||
import dotenv from "dotenv"; | ||
import fs from "fs/promises"; | ||
|
||
dotenv.config(); | ||
|
||
const prisma = new PrismaClient(); | ||
|
||
interface OutputRecord { | ||
to: string; | ||
name: string; | ||
shortcode: string; | ||
itemsToCollect: { | ||
rootitem: string; | ||
variant: string; | ||
quantity: number; | ||
}[]; | ||
subject: string; | ||
} | ||
|
||
async function getUncollectedPeople() { | ||
const uncollectedOrders = await prisma.orderItem.findMany({ | ||
where: { collected: false }, | ||
include: { | ||
Order: { | ||
include: { | ||
ImperialStudent: true, | ||
}, | ||
}, | ||
Variant: { | ||
include: { | ||
RootItem: true, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const outputRecords: OutputRecord[] = []; | ||
|
||
const studentMap = new Map<string, OutputRecord>(); | ||
|
||
for (const orderItem of uncollectedOrders) { | ||
const student = orderItem.Order.ImperialStudent; | ||
const studentKey = student.email; | ||
|
||
// Filter out if rootitem is "Pride Lanyard" | ||
// (in 2024 we started handing out these lanyard to whoever after the summer collection so | ||
/// it was likely that anyway who didn't collect it during merch collections got them during the random hand outs we did) | ||
if (orderItem.Variant.RootItem.name === "Pride Lanyard") { | ||
console.log(`Skipping ${studentKey} for Pride Lanyard`); | ||
continue; | ||
} | ||
|
||
if (!studentMap.has(studentKey)) { | ||
studentMap.set(studentKey, { | ||
to: student.email, | ||
name: `${student.firstName} ${student.lastName}`, | ||
shortcode: student.shortcode, | ||
itemsToCollect: [], | ||
subject: "Collect your remaining DoCSoc Summer Merch", | ||
}); | ||
} | ||
|
||
const studentRecord = studentMap.get(studentKey)!; | ||
studentRecord.itemsToCollect.push({ | ||
rootitem: orderItem.Variant.RootItem.name, | ||
variant: orderItem.Variant.variantName, | ||
quantity: orderItem.quantity, | ||
}); | ||
} | ||
|
||
outputRecords.push(...studentMap.values()); | ||
|
||
await fs.writeFile("data/reminders.json", JSON.stringify(outputRecords, null, 2)); | ||
} | ||
|
||
getUncollectedPeople() | ||
.catch((e) => { | ||
console.error(e); | ||
}) | ||
.finally(async () => { | ||
await prisma.$disconnect(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/** | ||
* You can safely ignore this file. It is used to merge the data from Jay's dump with the local database. | ||
* | ||
* Jay was a webmaster who got a old version of this system working on their machine, and we needed to import the collections from that DB into the working copy. | ||
*/ | ||
import { PrismaClient } from "@prisma/client"; | ||
import { parse } from "csv-parse"; | ||
import dotenv from "dotenv"; | ||
import fs from "fs/promises"; | ||
|
||
dotenv.config(); | ||
|
||
const prisma = new PrismaClient(); | ||
|
||
async function getStudentShortcode(orderItemId: number) { | ||
const orderItem = await prisma.orderItem.findUnique({ | ||
where: { id: orderItemId }, | ||
include: { | ||
Order: { | ||
include: { | ||
ImperialStudent: true, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
if (!orderItem) { | ||
throw new Error(`OrderItem with ID ${orderItemId} does not exist.`); | ||
} | ||
|
||
const studentShortcode = orderItem.Order.ImperialStudent.shortcode; | ||
return studentShortcode; | ||
} | ||
|
||
async function mergeJayDump() { | ||
// Load Jay's dump | ||
const jayDumpContent = await fs.readFile("data/jay-dump.csv", "utf-8"); | ||
const jayDumpRecords = await parse(jayDumpContent, { | ||
columns: true, | ||
delimiter: "\t", | ||
}); | ||
|
||
for await (const record of jayDumpRecords) { | ||
const orderItemId = parseInt(record.id, 10); | ||
const jayCollected = record.collected === "t"; | ||
|
||
// Check if the order item exists in the local database | ||
const localOrderItem = await prisma.orderItem.findUnique({ | ||
where: { id: orderItemId }, | ||
}); | ||
|
||
if (!localOrderItem) { | ||
// throw new Error( | ||
// `OrderItem with ID ${orderItemId} does not exist in the local database.`, | ||
// ); | ||
console.error(`OrderItem with ID ${orderItemId} does not exist in the local database!`); | ||
continue; | ||
} | ||
|
||
if (jayCollected && !localOrderItem.collected) { | ||
// Update the local database to mark the item as collected | ||
// await prisma.orderItem.update({ | ||
// where: { id: orderItemId }, | ||
// data: { collected: true }, | ||
// }); | ||
console.warn( | ||
`JAY TRUE, KISHAN FALSE: OrderItem with ID ${orderItemId} marked as collected in the local database. Shortcode: ${await getStudentShortcode( | ||
orderItemId, | ||
)}`, | ||
); | ||
|
||
// Print shortcode of the student | ||
} | ||
|
||
// if I have it marked as true, but Jay has it marked as false, report this | ||
if (localOrderItem.collected && !jayCollected) { | ||
console.warn( | ||
`KISHAN TRUE, JAY FALSE: OrderItem with ID ${orderItemId} marked as collected in the local database, but not in Jay's dump.`, | ||
); | ||
} | ||
} | ||
} | ||
|
||
mergeJayDump() | ||
.catch((e) => { | ||
console.error(e); | ||
}) | ||
.finally(async () => { | ||
await prisma.$disconnect(); | ||
}); |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Hi {{ name }}, | ||
|
||
You are yet to collect the following items that you bought from the DoCSoc summer merch drop last year: | ||
{% for item in itemsToCollect %} | ||
- {{ item.rootitem }} {{ item.variant }} x{{ item.quantity }} {% endfor %} | ||
|
||
You can collect them at any of the following collection dates: | ||
- Monday 21st Oct 4:15-6pm, Huxley 315 | ||
- Tuesday 22nd Oct 4:15-6pm, Huxley 315 | ||
- Friday 25th Oct 2pm-3:45pm, Huxley 218 | ||
- Monday 28th Oct 4:15pm-6pm, Huxley 315 | ||
- Thursday 31st Oct, 3:15pm-4:45pm, Huxley 315 | ||
- Monday 4th Nov, 4:15pm-6pm, Huxley 315 | ||
- Friday 8th Nov, 2:30pm-5pm, Huxley 218 | ||
|
||
If you have already collected your merch, didn’t buy any merch or otherwise believe this email was sent incorrectly, please let us know! | ||
|
||
Kind regards, | ||
DoCSoc Committee. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!-- DoCSoc Mail Merge Wrapper - used to wrap results rendered from Markdown --> | ||
<!-- You probably don't need to edit this file, but you can if you want to! --> | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
</head> | ||
<body> | ||
<!-- All HTML wrappers must provide a {{ content }} block to render the content fron the markdown--> | ||
{{ content }} | ||
</body> | ||
</html> |