From 08b736c6ddfdbc7f6b33f9e1102a2f588960524d Mon Sep 17 00:00:00 2001 From: "M.Palerme" Date: Wed, 4 Sep 2024 16:59:41 +0200 Subject: [PATCH] #48 calculate ratio for each metabolite and each project --- server/api/AssociateCalibCurves.post.ts | 3 + server/api/DisassociateCalibCurves.post.ts | 2 + server/api/extract.post.ts | 37 +++++------ server/api/function/RegCalibCurve.ts | 49 ++++++++++++-- server/api/function/updateRatio.ts | 77 ++++++++++++++++++++++ 5 files changed, 143 insertions(+), 25 deletions(-) create mode 100644 server/api/function/updateRatio.ts diff --git a/server/api/AssociateCalibCurves.post.ts b/server/api/AssociateCalibCurves.post.ts index abc6c1e..2d90120 100644 --- a/server/api/AssociateCalibCurves.post.ts +++ b/server/api/AssociateCalibCurves.post.ts @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT import pg from "pg"; +import { updateRatio } from "./function/updateRatio"; export default defineEventHandler(async (event) => { const body = await readBody(event); @@ -20,6 +21,8 @@ export default defineEventHandler(async (event) => { VALUES ${lValues.join(",")}` ) }) + // Update the ratio of the metabolite + .then(() => {updateRatio(body.id_project)}) .catch((err: Error) => { throw err; }) diff --git a/server/api/DisassociateCalibCurves.post.ts b/server/api/DisassociateCalibCurves.post.ts index 82d9d5c..5825378 100644 --- a/server/api/DisassociateCalibCurves.post.ts +++ b/server/api/DisassociateCalibCurves.post.ts @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT import pg from "pg"; +import { updateRatio } from "./function/updateRatio"; export default defineEventHandler((event) => { return readBody(event) @@ -15,6 +16,7 @@ export default defineEventHandler((event) => { WHERE id_calib_curves IN (${body.calibCurves.join(",")}) AND id_project = ${body.id_project}`)) + .then(() => {updateRatio(body.id_project)}) .finally(() => client.end()); }); }); \ No newline at end of file diff --git a/server/api/extract.post.ts b/server/api/extract.post.ts index 7718000..5b32486 100644 --- a/server/api/extract.post.ts +++ b/server/api/extract.post.ts @@ -34,46 +34,41 @@ async function exportFile(addressFile: { * @param indexMeta index of name of metabolite in sample * @param indexArea index of area in sample */ -function CalculateConcentration(sample: string[], lMolRatio: { [key: string]: number }, indexMeta:number, indexArea:number): string[] { +function CalculateConcentration(sample: string[], lMolRatio: { [key: string]: number[] }, indexMeta:number, indexArea:number): string[] { const tempSample = sample; // no metabolites in calibration curves associate of project if(lMolRatio[tempSample[indexMeta]] === undefined){ return [...tempSample, "0"]; } // calculate Concentration - tempSample.push((parseFloat(tempSample[indexArea]) * lMolRatio[tempSample[indexMeta]]).toString()); + tempSample.push(( + parseFloat(tempSample[indexArea]) * lMolRatio[tempSample[indexMeta]][0] + + lMolRatio[tempSample[indexMeta]][1]) + .toString()); return tempSample; } async function GetRatio(client: unknown, idProject: string): Promise<{[key:string]:number}> { - // get all calibration curves of project - return client.query(`SELECT id_calib_curves - FROM proj_calib_curves + // get all ratios of project + return client.query(`SELECT id_mol, coef, ord + FROM ratio WHERE id_project = ${idProject}`) - .then((respQuery: { rows: { id_calib_curves: string }[] }) => { + .then((respQuery: { rows: { + id_mol: string, + coef: string, + ord: string }[] } + ) => { if (respQuery.rows.length === 0) { throw new Error("No associated series"); } - const idCalibCurves = respQuery.rows.map(x => x.id_calib_curves); - // get all metabolite of calibration curves - return client.query(` - SELECT id_mol, ratio - FROM ratio - WHERE id_calib_curves IN (${idCalibCurves.join(",")})` - ); - }) - .then((respQuery: { rows: { id_mol: string, ratio: number }[] }) => { - - if (respQuery.rows.length === 0) { - throw new Error("No ratio"); - } // associate one metabolite to one ratio - const lMolRatio:{[key:string]:number} = {}; + const lMolRatio:{[key:string]:number[]} = {}; for (const row of respQuery.rows) { - lMolRatio[row.id_mol] = row.ratio; + lMolRatio[row.id_mol] = [parseFloat(row.coef), + parseFloat(row.ord)]; } return lMolRatio; diff --git a/server/api/function/RegCalibCurve.ts b/server/api/function/RegCalibCurve.ts index 31151bd..0b23110 100644 --- a/server/api/function/RegCalibCurve.ts +++ b/server/api/function/RegCalibCurve.ts @@ -22,25 +22,39 @@ export async function calculateRatioCalibCurve(arrayIdCalibCurve: string[]): Pro `) }) .then((res: { rows: any[] }) => { + console.log("meth", res.rows); + // create array area/concentration for each metabolite const metaConcentration: { [key: string]: [number, number][] } = {}; // Group the area and concentration of each metabolite - res.rows.map((row) => {metaConcentration[row.id_mol].push([row.area, row.concentration])}); + res.rows.forEach((row) => { + if (!metaConcentration[row.id_mol]) { + metaConcentration[row.id_mol] = []; + } + metaConcentration[row.id_mol].push([row.area, row.concentration]); + }); + console.log("metaConcentration0", metaConcentration); + // add the first element to 0,0 if we have one point for the // metabolite for (const key in metaConcentration) { - if (metaConcentration[key].length === 1) { + // We check after the deletion of the double point + if (deleteDoublePoint(metaConcentration[key]).length === 1) { metaConcentration[key].push([0, 0]); } } - + console.log("metaConcentration", metaConcentration); + // caclulate regression of each metabolite in ax + b const metaRatio: { [key: string]: {a: number, b: number} } = {} for (const key in metaConcentration) { // calculate the linear regression + console.log(key, metaConcentration[key]); + const { m, b } = linearRegression(metaConcentration[key]); metaRatio[key] = {a: m, b: b}; } + console.log("metaRatio", metaRatio); return metaRatio }) .catch(() => { @@ -50,4 +64,31 @@ export async function calculateRatioCalibCurve(arrayIdCalibCurve: string[]): Pro // close the connection client.end(); }); -} \ No newline at end of file +} + +/** + * Delete double point in the array + * @param array + * @returns array without double point + */ +function deleteDoublePoint(array: [number, number][]): [number, number][] { + // Track Seen Strings: A Set is used to keep track of the + // string representations of sub-arrays that have already been + // encountered. + const seen = new Set(); + return array.filter(subArray => { + // Convert Sub-Arrays to Strings: + const stringified = JSON.stringify(subArray); + // Check if the stringified sub-array has already been + // encountered: + if (seen.has(stringified)) { + // If it has been encountered, return false to remove + return false; + } else { + // keep the stringified sub-array in the set: + seen.add(stringified); + // keep the sub-array in the filtered array: + return true; + } + }); +} diff --git a/server/api/function/updateRatio.ts b/server/api/function/updateRatio.ts new file mode 100644 index 0000000..d3e8df1 --- /dev/null +++ b/server/api/function/updateRatio.ts @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 Marcellino Palerme +// +// SPDX-License-Identifier: MIT + +// This file provide function to add or update ratio of metabolite associate to project + +import pg from "pg"; +import { calculateRatioCalibCurve } from "./RegCalibCurve"; + +export async function updateRatio(id_project: string): Promise { + const client = new pg.Client(); + client.connect() + .then(() => { + // Get all calibration curve associate to the project + return client.query(` + SELECT id_calib_curves + FROM proj_calib_curves + WHERE id_project = '${id_project}' + `) + }) + .then((res: { rows: any[] }) => { + // Delete all ratio associate to the project if no + // calibration curve + if (res.rows.length === 0) { + client.query(` + DELETE FROM ratio + WHERE id_project = '${id_project}' + `); + return []; + } + + // structure the array of id_calib_curves + return res.rows.map((value : {id_calib_curves:string}) => + value.id_calib_curves); + }) + .then((arrayIdCalibCurve: string[]) => { + console.log(arrayIdCalibCurve); + + // Get the ratio of each metabolite of the calibration curve + return calculateRatioCalibCurve(arrayIdCalibCurve); + }) + .then((metaRatio: { [key: string]: {a: number, b: number} }) => { + console.log(metaRatio); + + // Update the ratio of each metabolite + for (const key in metaRatio) { + updateRatioMetabolite(key, id_project, metaRatio[key].a, metaRatio[key].b); + } + }) + .catch((err) => { + console.error("connection error", err.stack); + }) + .finally(() => { + client.end(); + }); + +} + +async function updateRatioMetabolite(id_mol: string, id_project: string, coef: number, ord: number): Promise { + const client = new pg.Client(); + client.connect() + .then(() => { + // Add or Update the ratio of the metabolite + const query = { + text: `INSERT INTO ratio (id_mol, id_project, coef, ord) VALUES ($1, $2, $3, $4) + ON CONFLICT (id_mol, id_project) DO UPDATE SET coef = $3, ord = $4`, + values: [id_mol, id_project, coef, ord], + }; + return client.query(query); + }) + .catch((err) => { + console.error("connection error", err.stack); + }) + .finally(() => { + client.end(); + }); +} \ No newline at end of file