Skip to content

Commit 276789f

Browse files
committed
feat: add a import script to collection for JSON data
1 parent 0b8eee9 commit 276789f

2 files changed

Lines changed: 305 additions & 0 deletions

File tree

collection/emails/import.ts

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* This scrpt allows you to import a list of people into the database under one specific items and variant.
3+
*
4+
* Auto imports into current academic year.
5+
*/
6+
import { AcademicYear } from "@docsoc/eactivities";
7+
import { createLogger } from "@docsoc/util";
8+
import { PrismaClient } from "@prisma/client";
9+
// Load .env file
10+
import dotenv from "dotenv";
11+
12+
const logger = createLogger("collection.import");
13+
14+
dotenv.config();
15+
16+
/**
17+
* ================================
18+
* DEFINE THE ITEM BELOW
19+
* ================================
20+
*/
21+
22+
/** Used for the name of the {@link RootItem} */
23+
const ROOT_ITEM_NAME = "Freshers Merch";
24+
25+
/** Used for the name of the {@link Variant} */
26+
const VARIANT_NAME = "Freshers Merch 2024";
27+
28+
/** Used for the quantity of the {@link Variant} */
29+
const VARIANT_QUANTITY = 1;
30+
31+
/**
32+
* ================================
33+
* DATA SOURCE
34+
* ================================
35+
*
36+
* Data source must be a JSON file
37+
*/
38+
/**
39+
* Data source must be a JSON file which is a list of records of this shape
40+
*/
41+
interface DataSource {
42+
/** MUST be correct as it is used as the unique ID in the DB to link orders to a studnt */
43+
shortcode: string;
44+
/** Ideally make this right, but if not just use the shortcode */
45+
cid: string;
46+
firstName: string;
47+
lastName: string;
48+
email: string;
49+
}
50+
51+
/** Path to the data source */
52+
const DATA_SOURCE_PATH = "./data/freshers-2024.json";
53+
54+
// Start the script
55+
56+
// 0: init db
57+
const prisma = new PrismaClient();
58+
59+
// 0.1: Find the current academic year
60+
// from collection/lib/config.ts
61+
async function getConfigValueFor(key: string) {
62+
const config = await prisma.config.findFirst({
63+
where: {
64+
key,
65+
},
66+
});
67+
return config?.value;
68+
}
69+
const ACADEMIC_YEAR_KEY = "academicYear";
70+
export async function getAcademicYear(): Promise<AcademicYear> {
71+
return (await getConfigValueFor(ACADEMIC_YEAR_KEY)) as string as AcademicYear;
72+
}
73+
74+
async function main() {
75+
const currentAcademicYear = await getAcademicYear();
76+
77+
if (!currentAcademicYear) {
78+
throw new Error("No academic year found to import into");
79+
}
80+
81+
// 1: Upsert the item
82+
83+
const item = await prisma.rootItem.upsert({
84+
where: { name: ROOT_ITEM_NAME },
85+
update: {},
86+
create: {
87+
name: ROOT_ITEM_NAME,
88+
academicYear: currentAcademicYear,
89+
Variant: {
90+
create: {
91+
variantName: VARIANT_NAME,
92+
},
93+
},
94+
},
95+
include: {
96+
Variant: true,
97+
},
98+
});
99+
100+
// Load file
101+
const data = (await import(DATA_SOURCE_PATH)).default as DataSource[];
102+
103+
// Valid data
104+
if (!Array.isArray(data)) {
105+
throw new Error("Data source is not an array");
106+
}
107+
108+
// Make an import for it
109+
const importName = `${ROOT_ITEM_NAME}, ${VARIANT_NAME} via import.ts @ ${new Date().toLocaleString(
110+
"en-GB",
111+
)}`;
112+
const importItem = await prisma.orderItemImport.create({
113+
data: {
114+
name: importName,
115+
},
116+
});
117+
118+
// 2: Upsert the students
119+
let index = 0;
120+
for (const record of data) {
121+
const { shortcode, cid, firstName, lastName, email } = record;
122+
123+
// If any null values, print warning and skip
124+
if (!shortcode || !cid || !firstName || !lastName || !email) {
125+
logger.warn(
126+
`Skipping ${shortcode} due to missing values. Record was ${JSON.stringify(record)}`,
127+
);
128+
continue;
129+
}
130+
131+
logger.debug(`Processing ${shortcode} <${email}> (CID: ${cid})`);
132+
logger.debug(`Upserting ${shortcode}...`);
133+
const student = await prisma.imperialStudent.upsert({
134+
where: {
135+
shortcode,
136+
},
137+
update: {},
138+
create: {
139+
cid,
140+
shortcode,
141+
firstName,
142+
lastName,
143+
email,
144+
},
145+
});
146+
147+
// Make order
148+
// If they already have a merch order matching that item, skip
149+
const existingOrder = await prisma.order.findFirst({
150+
where: {
151+
studentId: student.id,
152+
OrderItem: {
153+
some: {
154+
variantId: item.Variant[0].id,
155+
},
156+
},
157+
},
158+
});
159+
160+
if (existingOrder) {
161+
logger.warn(`Skipping ${shortcode} as they already have a ${VARIANT_NAME} order`);
162+
continue;
163+
}
164+
165+
// Create order
166+
await prisma.order.create({
167+
data: {
168+
academicYear: currentAcademicYear,
169+
orderDate: new Date(),
170+
studentId: student.id,
171+
orderNo: Math.floor(Math.random() * 100000) * 1000 + index,
172+
OrderItem: {
173+
create: {
174+
quantity: VARIANT_QUANTITY,
175+
variantId: item.Variant[0].id,
176+
importId: importItem.id,
177+
collected: false,
178+
},
179+
},
180+
},
181+
});
182+
183+
logger.info(`Imported ${shortcode} <${email}> (CID: ${cid})`);
184+
185+
index++;
186+
}
187+
}
188+
189+
main()
190+
.catch((e) => {
191+
console.error(e);
192+
process.exit(1);
193+
})
194+
.finally(() => {
195+
prisma.$disconnect();
196+
});

collection/emails/pride.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Import pride merch
2+
// OLD Script, will not work with current codebase
3+
import { PrismaClient } from "@prisma/client";
4+
import { parse } from "csv-parse";
5+
import { promises as fs } from "fs";
6+
7+
const prisma = new PrismaClient();
8+
9+
async function main(fileName: string) {
10+
// Create pride
11+
const lanyard = await prisma.rootItem.upsert({
12+
where: { name: "Pride Lanyard" },
13+
update: {},
14+
create: {
15+
name: "Pride Lanyard",
16+
variants: {
17+
create: {
18+
variantName: "Pride Lanyard",
19+
},
20+
},
21+
},
22+
include: {
23+
variants: true,
24+
},
25+
});
26+
27+
const fileContents = await fs.open(fileName, "r").then((f) => f.readFile("utf-8"));
28+
29+
const iter = parse(fileContents, {
30+
columns: true,
31+
});
32+
33+
for await (const record of iter) {
34+
const email = record["Email"];
35+
const name = record["Name"];
36+
const dateWithTime = record["Start time"];
37+
38+
const shortcode = email.split("@")[0];
39+
40+
console.log(`Processing ${name} <${email}> (${shortcode})`);
41+
42+
// User
43+
const user = await prisma.imperialStudent.upsert({
44+
where: {
45+
shortcode,
46+
},
47+
update: {},
48+
create: {
49+
cid: name,
50+
shortcode,
51+
firstName: name,
52+
lastName: "",
53+
email: email,
54+
},
55+
});
56+
57+
// Order
58+
// Basically, if they already have an order containing a pride lanyard, skip
59+
const lanyardOrders = await prisma.order.findFirst({
60+
where: {
61+
studentId: user.id,
62+
orderItems: {
63+
some: {
64+
variantId: lanyard.variants[0].id,
65+
},
66+
},
67+
},
68+
relationLoadStrategy: "join",
69+
include: {
70+
orderItems: true,
71+
},
72+
});
73+
74+
if (lanyardOrders) {
75+
console.log(`Skipping ${name} as they already have a lanyard order`);
76+
continue;
77+
}
78+
79+
// Add fake lanyard order
80+
await prisma.order.create({
81+
data: {
82+
orderDate: new Date(dateWithTime),
83+
student: {
84+
connect: {
85+
id: user.id,
86+
},
87+
},
88+
orderNo: Math.floor(Math.random() * 100000),
89+
orderItems: {
90+
create: {
91+
variantId: lanyard.variants[0].id,
92+
quantity: 1,
93+
collected: false,
94+
},
95+
},
96+
},
97+
});
98+
}
99+
}
100+
101+
main("./data/Pride Lanyard 26.06.24.csv")
102+
.then(async () => {
103+
await prisma.$disconnect();
104+
})
105+
.catch(async (e) => {
106+
console.error(e);
107+
await prisma.$disconnect();
108+
process.exit(1);
109+
});

0 commit comments

Comments
 (0)