diff --git a/collection/emails/import.ts b/collection/emails/import.ts new file mode 100644 index 0000000..67863fe --- /dev/null +++ b/collection/emails/import.ts @@ -0,0 +1,196 @@ +/** + * This scrpt allows you to import a list of people into the database under one specific items and variant. + * + * Auto imports into current academic year. + */ +import { AcademicYear } from "@docsoc/eactivities"; +import { createLogger } from "@docsoc/util"; +import { PrismaClient } from "@prisma/client"; +// Load .env file +import dotenv from "dotenv"; + +const logger = createLogger("collection.import"); + +dotenv.config(); + +/** + * ================================ + * DEFINE THE ITEM BELOW + * ================================ + */ + +/** Used for the name of the {@link RootItem} */ +const ROOT_ITEM_NAME = "Freshers Merch"; + +/** Used for the name of the {@link Variant} */ +const VARIANT_NAME = "Freshers Merch 2024"; + +/** Used for the quantity of the {@link Variant} */ +const VARIANT_QUANTITY = 1; + +/** + * ================================ + * DATA SOURCE + * ================================ + * + * Data source must be a JSON file + */ +/** + * Data source must be a JSON file which is a list of records of this shape + */ +interface DataSource { + /** MUST be correct as it is used as the unique ID in the DB to link orders to a studnt */ + shortcode: string; + /** Ideally make this right, but if not just use the shortcode */ + cid: string; + firstName: string; + lastName: string; + email: string; +} + +/** Path to the data source */ +const DATA_SOURCE_PATH = "./data/freshers-2024.json"; + +// Start the script + +// 0: init db +const prisma = new PrismaClient(); + +// 0.1: Find the current academic year +// from collection/lib/config.ts +async function getConfigValueFor(key: string) { + const config = await prisma.config.findFirst({ + where: { + key, + }, + }); + return config?.value; +} +const ACADEMIC_YEAR_KEY = "academicYear"; +export async function getAcademicYear(): Promise { + return (await getConfigValueFor(ACADEMIC_YEAR_KEY)) as string as AcademicYear; +} + +async function main() { + const currentAcademicYear = await getAcademicYear(); + + if (!currentAcademicYear) { + throw new Error("No academic year found to import into"); + } + + // 1: Upsert the item + + const item = await prisma.rootItem.upsert({ + where: { name: ROOT_ITEM_NAME }, + update: {}, + create: { + name: ROOT_ITEM_NAME, + academicYear: currentAcademicYear, + Variant: { + create: { + variantName: VARIANT_NAME, + }, + }, + }, + include: { + Variant: true, + }, + }); + + // Load file + const data = (await import(DATA_SOURCE_PATH)).default as DataSource[]; + + // Valid data + if (!Array.isArray(data)) { + throw new Error("Data source is not an array"); + } + + // Make an import for it + const importName = `${ROOT_ITEM_NAME}, ${VARIANT_NAME} via import.ts @ ${new Date().toLocaleString( + "en-GB", + )}`; + const importItem = await prisma.orderItemImport.create({ + data: { + name: importName, + }, + }); + + // 2: Upsert the students + let index = 0; + for (const record of data) { + const { shortcode, cid, firstName, lastName, email } = record; + + // If any null values, print warning and skip + if (!shortcode || !cid || !firstName || !lastName || !email) { + logger.warn( + `Skipping ${shortcode} due to missing values. Record was ${JSON.stringify(record)}`, + ); + continue; + } + + logger.debug(`Processing ${shortcode} <${email}> (CID: ${cid})`); + logger.debug(`Upserting ${shortcode}...`); + const student = await prisma.imperialStudent.upsert({ + where: { + shortcode, + }, + update: {}, + create: { + cid, + shortcode, + firstName, + lastName, + email, + }, + }); + + // Make order + // If they already have a merch order matching that item, skip + const existingOrder = await prisma.order.findFirst({ + where: { + studentId: student.id, + OrderItem: { + some: { + variantId: item.Variant[0].id, + }, + }, + }, + }); + + if (existingOrder) { + logger.warn(`Skipping ${shortcode} as they already have a ${VARIANT_NAME} order`); + continue; + } + + // Create order + await prisma.order.create({ + data: { + academicYear: currentAcademicYear, + orderDate: new Date(), + studentId: student.id, + orderNo: Math.floor(Math.random() * 100000) * 1000 + index, + OrderItem: { + create: { + quantity: VARIANT_QUANTITY, + variantId: item.Variant[0].id, + importId: importItem.id, + collected: false, + }, + }, + }, + }); + + logger.info(`Imported ${shortcode} <${email}> (CID: ${cid})`); + + index++; + } +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(() => { + prisma.$disconnect(); + }); diff --git a/collection/emails/pride.ts b/collection/emails/pride.ts new file mode 100644 index 0000000..1930d47 --- /dev/null +++ b/collection/emails/pride.ts @@ -0,0 +1,109 @@ +// Import pride merch +// OLD Script, will not work with current codebase +import { PrismaClient } from "@prisma/client"; +import { parse } from "csv-parse"; +import { promises as fs } from "fs"; + +const prisma = new PrismaClient(); + +async function main(fileName: string) { + // Create pride + const lanyard = await prisma.rootItem.upsert({ + where: { name: "Pride Lanyard" }, + update: {}, + create: { + name: "Pride Lanyard", + variants: { + create: { + variantName: "Pride Lanyard", + }, + }, + }, + include: { + variants: true, + }, + }); + + const fileContents = await fs.open(fileName, "r").then((f) => f.readFile("utf-8")); + + const iter = parse(fileContents, { + columns: true, + }); + + for await (const record of iter) { + const email = record["Email"]; + const name = record["Name"]; + const dateWithTime = record["Start time"]; + + const shortcode = email.split("@")[0]; + + console.log(`Processing ${name} <${email}> (${shortcode})`); + + // User + const user = await prisma.imperialStudent.upsert({ + where: { + shortcode, + }, + update: {}, + create: { + cid: name, + shortcode, + firstName: name, + lastName: "", + email: email, + }, + }); + + // Order + // Basically, if they already have an order containing a pride lanyard, skip + const lanyardOrders = await prisma.order.findFirst({ + where: { + studentId: user.id, + orderItems: { + some: { + variantId: lanyard.variants[0].id, + }, + }, + }, + relationLoadStrategy: "join", + include: { + orderItems: true, + }, + }); + + if (lanyardOrders) { + console.log(`Skipping ${name} as they already have a lanyard order`); + continue; + } + + // Add fake lanyard order + await prisma.order.create({ + data: { + orderDate: new Date(dateWithTime), + student: { + connect: { + id: user.id, + }, + }, + orderNo: Math.floor(Math.random() * 100000), + orderItems: { + create: { + variantId: lanyard.variants[0].id, + quantity: 1, + collected: false, + }, + }, + }, + }); + } +} + +main("./data/Pride Lanyard 26.06.24.csv") + .then(async () => { + await prisma.$disconnect(); + }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + });