Compare commits
2 Commits
885e694ad2
...
b40d0aedb4
| Author | SHA1 | Date | |
|---|---|---|---|
| b40d0aedb4 | |||
| cb6dd05071 |
6
importersFixed.md
Normal file
6
importersFixed.md
Normal file
@ -0,0 +1,6 @@
|
||||
- [ ] Import users
|
||||
- [x] Import artefacts
|
||||
- [ ] Import earthquakes
|
||||
- [ ] Import observatoies
|
||||
- [ ] Import requests
|
||||
- [ ] Import scientists
|
||||
@ -91,6 +91,7 @@ model Artefact {
|
||||
type String @db.VarChar(50) // Lava, Tephra, Ash, Soil
|
||||
warehouseArea String
|
||||
description String
|
||||
imageName String
|
||||
earthquakeId Int
|
||||
earthquake Earthquake @relation(fields: [earthquakeId], references: [id])
|
||||
creatorId Int?
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Name,Type,WarehouseArea,Description,earthquakeID,Price,Required,PickedUp,Picture
|
||||
Name,Type,WarehouseArea,Description,EarthquakeCode,Price,Required,PickedUp,Picture
|
||||
Echo Bomb,Lava,ShelvingAreaA,A dense glossy black volcanic bomb with minor vesicles.,EV-7.4-Mexico-00035,120,no,no,EchoBomb.PNG
|
||||
Silvershade Ash,Ash,ShelvingAreaD,Fine light-grey volcanic ash collected near a village.,EV-6.0-Iceland-00018,40,no,no,SilvershadeAsh.PNG
|
||||
Strata Core,Soil,LoggingArea,Soil core with visible stratification showing evidence of liquefaction.,ET-6.9-Brazil-00046,30,no,no,StrataCore.PNG
|
||||
|
||||
|
87
src/app/api/import-artefacts/route.ts
Normal file
87
src/app/api/import-artefacts/route.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { parse } from "csv-parse/sync";
|
||||
import fs from "fs/promises";
|
||||
import { NextResponse } from "next/server";
|
||||
import path from "path";
|
||||
import { stringToBool } from "@utils/parsingUtils";
|
||||
import { prisma } from "@utils/prisma";
|
||||
import { getRandomNumber } from "@utils/maths";
|
||||
|
||||
const csvFilePath = path.resolve(process.cwd(), "public/artefacts.csv");
|
||||
|
||||
type CsvRow = {
|
||||
Type: string;
|
||||
Name: string;
|
||||
Description: string;
|
||||
WarehouseArea: string;
|
||||
EarthquakeCode: string;
|
||||
Required?: string;
|
||||
Price: string;
|
||||
PickedUp?: string;
|
||||
Picture: string;
|
||||
};
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
const fileContent = await fs.readFile(csvFilePath, "utf8");
|
||||
const records: CsvRow[] = parse(fileContent, {
|
||||
columns: true,
|
||||
skip_empty_lines: true,
|
||||
});
|
||||
|
||||
const failedImports: { row: CsvRow; reason: string }[] = [];
|
||||
|
||||
const artefacts = await Promise.all(
|
||||
records.map(async (row) => {
|
||||
const earthquake = await prisma.earthquake.findUnique({
|
||||
where: { code: row.EarthquakeCode },
|
||||
});
|
||||
|
||||
const creators = await prisma.user.findMany({
|
||||
where: {
|
||||
role: { in: ["SCIENTIST", "ADMIN"] },
|
||||
},
|
||||
});
|
||||
const randomCreator = creators.length > 0 ? creators[getRandomNumber(0, creators.length - 1)] : null;
|
||||
|
||||
if (!earthquake || !randomCreator) {
|
||||
failedImports.push({ row, reason: `Earthquake: ${earthquake}, RandomCreator: ${randomCreator}` });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
name: row.Name,
|
||||
description: row.Description,
|
||||
type: row.Type,
|
||||
warehouseArea: row.WarehouseArea,
|
||||
earthquakeId: earthquake.id,
|
||||
required: stringToBool(row.Required, true),
|
||||
shopPrice: row.Price && row.Price !== "" ? parseFloat(row.Price) : getRandomNumber(20, 500),
|
||||
pickedUp: stringToBool(row.PickedUp, false),
|
||||
creatorId: randomCreator.id,
|
||||
purchasedById: null,
|
||||
imageName: row.Picture,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const validArtefacts = artefacts.filter((artefact): artefact is NonNullable<typeof artefact> => artefact !== undefined);
|
||||
|
||||
await prisma.artefact.createMany({
|
||||
data: validArtefacts,
|
||||
});
|
||||
|
||||
if (failedImports.length > 0) {
|
||||
console.warn("Failed imports:", failedImports);
|
||||
await fs.writeFile(path.resolve(process.cwd(), "failed_imports_artefacts.json"), JSON.stringify(failedImports, null, 2));
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
count: validArtefacts.length,
|
||||
failedCount: failedImports.length,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
import { parse } from "csv-parse/sync";
|
||||
import fs from "fs/promises";
|
||||
import { NextResponse } from "next/server";
|
||||
import path from "path";
|
||||
|
||||
import { PrismaClient } from "@prismaclient";
|
||||
|
||||
// CSV location
|
||||
const csvFilePath = path.resolve(process.cwd(), "public/artefacts.csv");
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
type CsvRow = {
|
||||
Type: string;
|
||||
Name: string;
|
||||
Description: string;
|
||||
WarehouseArea: string;
|
||||
EarthquakeId: string;
|
||||
Required?: string;
|
||||
ShopPrice?: string;
|
||||
PickedUp?: string;
|
||||
};
|
||||
|
||||
function stringToBool(val: string | undefined, defaultValue: boolean = false): boolean {
|
||||
if (!val) return defaultValue;
|
||||
return /^true$/i.test(val.trim());
|
||||
}
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
// 1. Read file
|
||||
const fileContent = await fs.readFile(csvFilePath, "utf8");
|
||||
|
||||
// 2. Parse CSV
|
||||
const records: CsvRow[] = parse(fileContent, {
|
||||
columns: true,
|
||||
skip_empty_lines: true,
|
||||
});
|
||||
|
||||
// 3. Map records to artefact input
|
||||
const artefacts = records.map((row) => ({
|
||||
name: row.Name,
|
||||
description: row.Description,
|
||||
type: row.Type,
|
||||
warehouseArea: row.WarehouseArea,
|
||||
// todo get earthquakeId where code === row.EarthquakeCode
|
||||
earthquakeId: parseInt(row.EarthquakeId, 10),
|
||||
required: stringToBool(row.Required, true), // default TRUE
|
||||
shopPrice: row.ShopPrice && row.ShopPrice !== "" ? parseFloat(row.ShopPrice) : null,
|
||||
pickedUp: stringToBool(row.PickedUp, false), // default FALSE
|
||||
// todo add random selection for creatorId
|
||||
creatorId: null,
|
||||
purchasedById: null,
|
||||
}));
|
||||
|
||||
// 4. Bulk insert
|
||||
await prisma.artefact.createMany({
|
||||
data: artefacts,
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
count: artefacts.length,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
|
||||
const OurMission = () => {
|
||||
function OurMission() {
|
||||
return (
|
||||
<div
|
||||
className="relative bg-fixed bg-cover bg-center text-white "
|
||||
style={{ backgroundImage: "url('destruction.jpg')", height: 845, overflow: "hidden" }}
|
||||
className="relative h-full bg-fixed bg-cover bg-center text-white "
|
||||
style={{ backgroundImage: "url('destruction.jpg')", overflow: "hidden" }}
|
||||
>
|
||||
{/* Overlay for Readability */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-50"></div>
|
||||
@ -55,5 +55,5 @@ const OurMission = () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
export default OurMission;
|
||||
|
||||
@ -214,7 +214,7 @@ export default function Shop() {
|
||||
if (currentPage > 1) setCurrentPage((prev) => prev - 1);
|
||||
};
|
||||
|
||||
function ArtefactCard({ artefact }: { artefact: Artefact }) {
|
||||
function ArtefactCard({ artefact }: { artefact: ExtendedArtefact }) {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col bg-white shadow-md rounded-md overflow-hidden cursor-pointer hover:scale-105 transition-transform"
|
||||
@ -233,7 +233,7 @@ export default function Shop() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function Modal({ artefact }: { artefact: Artefact }) {
|
||||
function Modal({ artefact }: { artefact: ExtendedArtefact }) {
|
||||
if (!artefact) return null;
|
||||
const handleOverlayClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) setSelectedArtefact(null);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Action } from "easy-peasy";
|
||||
// import type { User } from "@prisma/client";
|
||||
// import type { User } from "@prismaclient";
|
||||
import { User } from "@appTypes/Prisma";
|
||||
|
||||
type Currency = "GBP" | "USD" | "EUR";
|
||||
|
||||
5
src/utils/maths.ts
Normal file
5
src/utils/maths.ts
Normal file
@ -0,0 +1,5 @@
|
||||
function getRandomNumber(min: number, max: number): number {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
export { getRandomNumber };
|
||||
7
src/utils/parsingUtils.ts
Normal file
7
src/utils/parsingUtils.ts
Normal file
@ -0,0 +1,7 @@
|
||||
function stringToBool(val: string | undefined, defaultValue: boolean = false): boolean {
|
||||
if (!val) return defaultValue;
|
||||
const normalized = val.trim().toLowerCase();
|
||||
return normalized === "true" || normalized === "yes";
|
||||
}
|
||||
|
||||
export { stringToBool };
|
||||
@ -1,9 +1,9 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { PrismaClient } from "@prismaclient";
|
||||
|
||||
declare global {
|
||||
var prisma: PrismaClient | undefined;
|
||||
}
|
||||
|
||||
const prisma = process.env.NODE_ENV === "production" ? new PrismaClient() : global.prisma ?? (global.prisma = new PrismaClient());
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export { prisma };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user