From 3e5169b454a98ee0773504555ddfdea6a50681fb Mon Sep 17 00:00:00 2001 From: Akhil Meka Date: Tue, 9 Sep 2025 19:26:52 +0530 Subject: [PATCH] feat: add new role 'bot' --- src/fileValidation/ai.ts | 142 --------------------------- src/fileValidation/validate.route.ts | 26 ----- src/fileValidation/validate.ts | 85 ---------------- src/note/note.service.ts | 15 --- src/utils/roles.ts | 4 + 5 files changed, 4 insertions(+), 268 deletions(-) delete mode 100644 src/fileValidation/ai.ts delete mode 100644 src/fileValidation/validate.route.ts delete mode 100644 src/fileValidation/validate.ts diff --git a/src/fileValidation/ai.ts b/src/fileValidation/ai.ts deleted file mode 100644 index 1332133..0000000 --- a/src/fileValidation/ai.ts +++ /dev/null @@ -1,142 +0,0 @@ -import OpenAI from "openai"; -import fsp from "fs/promises"; -import { PdfReader } from "pdfreader"; - -const openaiClient = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, -}); - -async function queryChatGPT(prompt: string) { - let res = await openaiClient.chat.completions.create({ - model: "gpt-4o", - messages: [ - { - role: "system", - content: prompt, - }, - ], - response_format: { type: "json_object" }, - }); - - return JSON.parse(res.choices[0].message.content); -} - -async function parseBufferAsync(buffer: Buffer): Promise { - let parsedData = ""; - - return new Promise((resolve, reject) => { - new PdfReader().parseBuffer(buffer, (error, item) => { - if (error) return reject(error); - if (!item) return resolve(parsedData); - if (item.text) parsedData += item.text; - }); - }); -} - -export default async function generateNote( - folderPath: string -): Promise { - let energyData = {}; - let checklistData = {}; - let energyDataFlag = false; - let checklistDataFlag = false; - let energyFilePath = ""; - let checklistFilePath = ""; - - const files = await fsp.readdir(folderPath); - - for (const file of files) { - const input = await fsp.readFile(folderPath + `/${file}`); - const pdfData = await parseBufferAsync(input); - - if ( - Object.keys(checklistData).length == 0 && - pdfData.toLowerCase().includes("general contractors") - ) { - const prompt = ` - Extract the following fields and contractor info from the given data and return the response in JSON. - Fields: ["Site Address", "Lot Number", "Estimated Construction Cost", "Total Square Footage", "A/C Square Footage", "Number of Bedrooms", "Number of Bathrooms", "Construction Type", "Number of Stories", "Height", "General Contractors License", "Mechanical Contractors License", "Electrical Contractors License", "Plumbing Contractors License", "Roofing Contractors License", "Concrete/Mason Contractors License", "Confirm the following"] - - Data: ${pdfData} - `; - - checklistDataFlag = true; - checklistFilePath = file; - checklistData = await queryChatGPT(prompt); - } - - if ( - Object.keys(energyData).length == 0 && - pdfData.toLowerCase().includes("energy efficiency") - ) { - const prompt = ` - Extract the following fields form the given data and return the response in JSON. - Fields: [Project Name, address, county] - - Data: ${pdfData} - `; - - energyDataFlag = true; - energyFilePath = file; - energyData = await queryChatGPT(prompt); - } - } - - const addressCheck = {}; - - if (checklistDataFlag && Object.keys(checklistData).length > 0) { - for (const file of files) { - if (file == checklistFilePath) continue; - - const input = await fsp.readFile(folderPath + `${file}`); - const pdfData = await parseBufferAsync(input); - - const prompt = ` - Check if the address in the data given below matches this ${checklistData["Site Address"]}. Return the response in JSON with schema {match: Boolean}. - - Data: ${pdfData} - `; - - const { match } = await queryChatGPT(prompt); - addressCheck[file] = match; - } - } - - let note = ""; - - if (!energyDataFlag) { - note += "Energy Efficiency document not found.\n"; - } - - if (!checklistDataFlag) { - note += `Checklist document not found.\n\n`; - } - - let filesList = ""; - for (const file in addressCheck) { - if (!addressCheck[file]) { - filesList += file.split("/").pop() + "\n"; - } - } - - if (filesList != "") { - note += - "Below files don't have address or the address doesn't match with the address in checklist\n\n"; - note += filesList; - note += "\n"; - } - - if (energyDataFlag || checklistDataFlag) { - note += "\nExtracted Data:\n"; - } - - if (Object.keys(energyData).length > 0) { - note += `\n${JSON.stringify(energyData, null, 2)}\n`; - } - - if (Object.keys(checklistData).length > 0) { - note += `\n${JSON.stringify(checklistData, null, 2)}\n`; - } - - return note; -} diff --git a/src/fileValidation/validate.route.ts b/src/fileValidation/validate.route.ts deleted file mode 100644 index 2ae7ad8..0000000 --- a/src/fileValidation/validate.route.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; -import { validate } from "./validate"; - -export async function validateRoutes(fastify: FastifyInstance) { - fastify.post( - "/:rtsId", - { - schema: { - params: { - type: "object", - properties: { rtsId: { type: "string" } }, - }, - }, - }, - async (req: FastifyRequest, res: FastifyReply) => { - const { rtsId } = req.params as { rtsId: string }; - - try { - validate(rtsId, req.user.tenantId); - return res.code(200).send(); - } catch (err) { - return err; - } - } - ); -} diff --git a/src/fileValidation/validate.ts b/src/fileValidation/validate.ts deleted file mode 100644 index e63728f..0000000 --- a/src/fileValidation/validate.ts +++ /dev/null @@ -1,85 +0,0 @@ -import fsp from "fs/promises"; -import axios from "axios"; -import { rtsModel } from "../rts/rts.schema"; -import { getChildren } from "../file/file.service"; -import { getFileUrlS3 } from "../utils/s3"; -import generateNote from "./ai"; -import { createNote, createNoteBot } from "../note/note.service"; - -async function downloadFile(url: string, downloadPath: string) { - try { - const res = await axios({ - url: url, - method: "GET", - responseType: "stream", - }); - - await fsp.writeFile(downloadPath, res.data); - } catch (err) { - console.log(err); - } -} - -async function downloadFileTree( - basePath: string, - recId: string, - tenantId: string -) { - const rts = await rtsModel.findOne({ pid: recId }); - - async function downloadFolder(folderId: string, path: string) { - const items = await getChildren(folderId, tenantId); - for (const item of items) { - if (item.mimeType == "folder") { - const newPath = path + `/${item.name}`; - await fsp.mkdir(newPath); - await downloadFolder(item.pid, newPath); - } else { - await downloadFile( - await getFileUrlS3(item.pid, null, false), - path + `/${item.name}` - ); - } - } - } - - await downloadFolder(recId, basePath); -} - -async function deleteFolder(path: string) { - await fsp.rm(path, { recursive: true, force: true }); -} - -export async function validate(recId: string, tenantId: string) { - const basePath = process.env.BASE_PATH || "/root/tmp/quickerPermit"; - const folderPath = basePath + `/${recId}`; - - try { - await fsp.mkdir(folderPath); - await downloadFileTree(folderPath, recId, tenantId); - - const files = await fsp.readdir(folderPath); - - const notes: { folder: string; note: string }[] = []; - for (const file of files) { - const filePath = folderPath + `/${file}`; - const stats = await fsp.stat(filePath); - if (stats.isDirectory()) { - const note = await generateNote(filePath); - notes.push({ folder: file, note }); - } - } - - let finalNote = ""; - for (const note of notes) { - finalNote += `${note.folder}\n`; - finalNote += `${note.note}\n\n`; - } - - await createNoteBot(finalNote, recId, tenantId); - } catch (err) { - console.log(err); - } finally { - deleteFolder(folderPath); - } -} diff --git a/src/note/note.service.ts b/src/note/note.service.ts index cb4296a..f9afdf0 100644 --- a/src/note/note.service.ts +++ b/src/note/note.service.ts @@ -80,21 +80,6 @@ export async function createNote( return newNote.populate({ path: "createdBy", select: "pid name avatar" }); } -export async function createNoteBot( - content: string, - resourceId: string, - tenantId: string -) { - const newNote = await noteModel.create({ - tenantId: tenantId, - pid: generateId(), - resourceId: resourceId, - content: content, - createdAt: new Date(), - createdBy: "6762acd606db9d07307a302d", - }); -} - export async function updateNote( input: CreateNoteInput, resourceId: string, diff --git a/src/utils/roles.ts b/src/utils/roles.ts index 2fa305e..1dfd38a 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -163,6 +163,10 @@ export const rules: Record< users: ["__v"], }, }, + bot: { + claims: ["file:download", "note:write"], + hiddenFields: {}, + }, }; export const roles = Object.keys(rules) as [