Skip to content

Commit 0b38f66

Browse files
committed
fixing issues with file upload and processing system. bugs were found causing load issues and SQL overload issues, but have been temporarily resolved. refit is needed to update how the file set is processed to ensure that system will not collapse under weight of file set.
1 parent 69c8436 commit 0b38f66

File tree

13 files changed

+207
-95
lines changed

13 files changed

+207
-95
lines changed

frontend/app/(hub)/measurementshub/validations/page.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ export default function ValidationsPage() {
6262
try {
6363
const response = await fetch('/api/validations/crud', { method: 'GET' });
6464
const data = await response.json();
65-
console.log('data: ', data);
6665
setGlobalValidations(data);
6766
} catch (err) {
6867
console.error('Error fetching validations:', err);

frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts

-1
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,6 @@ export async function DELETE(request: NextRequest, { params }: { params: { dataT
365365
let conn: PoolConnection | null = null;
366366
const demappedGridID = gridID.charAt(0).toUpperCase() + gridID.substring(1);
367367
const { newRow } = await request.json();
368-
console.log('newrow: ', newRow);
369368
try {
370369
conn = await getConn();
371370
await conn.beginTransaction();

frontend/app/api/validations/procedures/[validationType]/route.ts

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { HTTPResponses } from '@/config/macros';
55
export async function POST(request: NextRequest, { params }: { params: { validationProcedureName: string } }) {
66
try {
77
const { schema, validationProcedureID, cursorQuery, p_CensusID, p_PlotID, minDBH, maxDBH, minHOM, maxHOM } = await request.json();
8-
console.log('data: ', schema, validationProcedureID, cursorQuery, p_CensusID, p_PlotID, minDBH, maxDBH, minHOM, maxHOM);
98

109
// Execute the validation procedure using the provided inputs
1110
const validationResponse = await runValidation(validationProcedureID, params.validationProcedureName, schema, cursorQuery, {

frontend/components/datagrids/isolateddatagridcommons.tsx

+76-4
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,25 @@ export default function IsolatedDataGridCommons(props: Readonly<IsolatedDataGrid
241241
aData.forEach((row: any) => {
242242
const values = getTableHeaders(FormType.attributes)
243243
.map(rowHeader => rowHeader.label)
244-
.map(header => row[header]);
244+
.map(header => row[header])
245+
.map(value => {
246+
if (value === undefined || value === null || value === '') {
247+
return null;
248+
}
249+
if (typeof value === 'number') {
250+
return value;
251+
}
252+
const parsedValue = parseFloat(value);
253+
if (!isNaN(parsedValue)) {
254+
return parsedValue;
255+
}
256+
if (typeof value === 'string') {
257+
value = value.replace(/"/g, '""');
258+
value = `"${value}"`;
259+
}
260+
261+
return value;
262+
});
245263
aCSVRows += values.join(',') + '\n';
246264
});
247265
const aBlob = new Blob([aCSVRows], {
@@ -268,7 +286,25 @@ export default function IsolatedDataGridCommons(props: Readonly<IsolatedDataGrid
268286
qData.forEach((row: any) => {
269287
const values = getTableHeaders(FormType.quadrats)
270288
.map(rowHeader => rowHeader.label)
271-
.map(header => row[header]);
289+
.map(header => row[header])
290+
.map(value => {
291+
if (value === undefined || value === null || value === '') {
292+
return null;
293+
}
294+
if (typeof value === 'number') {
295+
return value;
296+
}
297+
const parsedValue = parseFloat(value);
298+
if (!isNaN(parsedValue)) {
299+
return parsedValue;
300+
}
301+
if (typeof value === 'string') {
302+
value = value.replace(/"/g, '""');
303+
value = `"${value}"`;
304+
}
305+
306+
return value;
307+
});
272308
qCSVRows += values.join(',') + '\n';
273309
});
274310
const qBlob = new Blob([qCSVRows], {
@@ -295,7 +331,25 @@ export default function IsolatedDataGridCommons(props: Readonly<IsolatedDataGrid
295331
pData.forEach((row: any) => {
296332
const values = getTableHeaders(FormType.personnel)
297333
.map(rowHeader => rowHeader.label)
298-
.map(header => row[header]);
334+
.map(header => row[header])
335+
.map(value => {
336+
if (value === undefined || value === null || value === '') {
337+
return null;
338+
}
339+
if (typeof value === 'number') {
340+
return value;
341+
}
342+
const parsedValue = parseFloat(value);
343+
if (!isNaN(parsedValue)) {
344+
return parsedValue;
345+
}
346+
if (typeof value === 'string') {
347+
value = value.replace(/"/g, '""');
348+
value = `"${value}"`;
349+
}
350+
351+
return value;
352+
});
299353
pCSVRows += values.join(',') + '\n';
300354
});
301355
const pBlob = new Blob([pCSVRows], {
@@ -323,7 +377,25 @@ export default function IsolatedDataGridCommons(props: Readonly<IsolatedDataGrid
323377
sData.forEach((row: any) => {
324378
const values = getTableHeaders(FormType.species)
325379
.map(rowHeader => rowHeader.label)
326-
.map(header => row[header]);
380+
.map(header => row[header])
381+
.map(value => {
382+
if (value === undefined || value === null || value === '') {
383+
return null;
384+
}
385+
if (typeof value === 'number') {
386+
return value;
387+
}
388+
const parsedValue = parseFloat(value);
389+
if (!isNaN(parsedValue)) {
390+
return parsedValue;
391+
}
392+
if (typeof value === 'string') {
393+
value = value.replace(/"/g, '""');
394+
value = `"${value}"`;
395+
}
396+
397+
return value;
398+
});
327399
sCSVRows += values.join(',') + '\n';
328400
});
329401
const sBlob = new Blob([sCSVRows], {

frontend/components/datagrids/isolatedmultilinedatagridcommons.tsx

+31-24
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export default function IsolatedMultilineDataGridCommons(props: Readonly<Isolate
4747
rowsBeforeChange: {}
4848
});
4949

50-
const columns = useMemo<GridColDef[]>(
51-
() => [
50+
const columns = useMemo<GridColDef[]>(() => {
51+
let baseColumns: GridColDef[] = [
5252
{
5353
field: 'actions',
5454
headerName: 'Actions',
@@ -80,33 +80,40 @@ export default function IsolatedMultilineDataGridCommons(props: Readonly<Isolate
8080
unsavedChangesRef.current.rowsBeforeChange[id] = row;
8181
}
8282
setHasUnsavedRows(true);
83-
apiRef.current.updateRows([row]); // to trigger row render
83+
apiRef.current.updateRows([row]);
8484
}}
8585
/>
8686
];
8787
}
8888
},
89-
...gridColumns,
90-
{
91-
field: 'date',
92-
headerName: 'Date',
93-
headerClassName: 'header',
94-
flex: 1,
95-
editable: true,
96-
renderCell: renderDatePicker,
97-
renderEditCell: renderEditDatePicker
98-
},
99-
{
100-
field: 'codes',
101-
headerName: 'Codes',
102-
headerClassName: 'header',
103-
flex: 1,
104-
align: 'center',
105-
editable: true
106-
}
107-
],
108-
[gridColumns, unsavedChangesRef, apiRef, setRows]
109-
);
89+
...gridColumns
90+
];
91+
92+
if (gridType === 'measurements') {
93+
baseColumns = [
94+
...baseColumns,
95+
{
96+
field: 'date',
97+
headerName: 'Date',
98+
headerClassName: 'header',
99+
flex: 1,
100+
editable: true,
101+
renderCell: renderDatePicker,
102+
renderEditCell: renderEditDatePicker
103+
},
104+
{
105+
field: 'codes',
106+
headerName: 'Codes',
107+
headerClassName: 'header',
108+
flex: 1,
109+
align: 'center',
110+
editable: true
111+
}
112+
];
113+
}
114+
115+
return baseColumns;
116+
}, [gridColumns, gridType, unsavedChangesRef, apiRef, setHasUnsavedRows]);
110117

111118
const processRowUpdate = useCallback<NonNullable<DataGridProps['processRowUpdate']>>((newRow, oldRow) => {
112119
const rowId = newRow.id;

frontend/components/datagrids/measurementscommons.tsx

+19-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,25 @@ export default function MeasurementsCommons(props: Readonly<MeasurementsCommonsP
209209
data.forEach((row: any) => {
210210
const values = getTableHeaders(FormType.measurements)
211211
.map(rowHeader => rowHeader.label)
212-
.map(header => row[header]);
212+
.map(header => row[header])
213+
.map(value => {
214+
if (value === undefined || value === null || value === '') {
215+
return null;
216+
}
217+
if (typeof value === 'number') {
218+
return value;
219+
}
220+
const parsedValue = parseFloat(value);
221+
if (!isNaN(parsedValue)) {
222+
return parsedValue;
223+
}
224+
if (typeof value === 'string') {
225+
value = value.replace(/"/g, '""');
226+
value = `"${value}"`;
227+
}
228+
229+
return value;
230+
});
213231
csvRows += values.join(',') + '\n';
214232
});
215233
const blob = new Blob([csvRows], {

frontend/components/processors/processcensus.tsx

+41-45
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,20 @@ export async function processCensus(props: Readonly<SpecialProcessingProps>): Pr
1111
console.error('Missing required parameters: plotID or censusID');
1212
throw new Error('Process Census: Missing plotID or censusID');
1313
}
14-
1514
const { tag, stemtag, spcode, quadrat, lx, ly, coordinateunit, dbh, dbhunit, hom, homunit, date, codes } = rowData;
1615

1716
try {
1817
await connection.beginTransaction();
19-
2018
// Fetch species
2119
const speciesID = await fetchPrimaryKey<SpeciesResult>(schema, 'species', { SpeciesCode: spcode }, connection, 'SpeciesID');
22-
2320
// Fetch quadrat
2421
const quadratID = await fetchPrimaryKey<QuadratResult>(schema, 'quadrats', { QuadratName: quadrat, PlotID: plotID }, connection, 'QuadratID');
2522

2623
if (tag) {
2724
// Handle Tree Upsert
2825
const treeID = await handleUpsert<TreeResult>(connection, schema, 'trees', { TreeTag: tag, SpeciesID: speciesID }, 'TreeID');
2926

30-
if (stemtag && lx && ly) {
31-
console.log('Processing stem with StemTag:', stemtag);
27+
if (stemtag || lx || ly) {
3228
// Handle Stem Upsert
3329
const stemID = await handleUpsert<StemResult>(
3430
connection,
@@ -38,46 +34,47 @@ export async function processCensus(props: Readonly<SpecialProcessingProps>): Pr
3834
'StemID'
3935
);
4036

41-
if (dbh && hom && date) {
42-
// Handle Core Measurement Upsert
43-
const coreMeasurementID = await handleUpsert<CoreMeasurementsResult>(
44-
connection,
45-
schema,
46-
'coremeasurements',
47-
{
48-
CensusID: censusID,
49-
StemID: stemID,
50-
IsValidated: null,
51-
MeasurementDate: moment(date).format('YYYY-MM-DD'),
52-
MeasuredDBH: dbh,
53-
DBHUnit: dbhunit,
54-
MeasuredHOM: hom,
55-
HOMUnit: homunit
56-
},
57-
'CoreMeasurementID'
58-
);
37+
// Handle Core Measurement Upsert
38+
const coreMeasurementID = await handleUpsert<CoreMeasurementsResult>(
39+
connection,
40+
schema,
41+
'coremeasurements',
42+
{
43+
CensusID: censusID,
44+
StemID: stemID,
45+
IsValidated: null,
46+
MeasurementDate: date && moment(date).isValid() ? moment.utc(date).format('YYYY-MM-DD') : null,
47+
MeasuredDBH: dbh ? parseFloat(dbh) : null,
48+
DBHUnit: dbhunit,
49+
MeasuredHOM: hom ? parseFloat(hom) : null,
50+
HOMUnit: homunit,
51+
Description: null,
52+
UserDefinedFields: null
53+
},
54+
'CoreMeasurementID'
55+
);
5956

60-
// Handle CM Attributes Upsert
61-
if (codes) {
62-
const parsedCodes = codes
63-
.split(';')
64-
.map(code => code.trim())
65-
.filter(Boolean);
66-
if (parsedCodes.length === 0) {
67-
console.error('No valid attribute codes found:', codes);
68-
} else {
69-
for (const code of parsedCodes) {
70-
const attributeRows = await runQuery(connection, `SELECT COUNT(*) as count FROM ${schema}.attributes WHERE Code = ?`, [code]);
71-
if (!attributeRows || attributeRows.length === 0 || !attributeRows[0].count) {
72-
throw createError(`Attribute code ${code} not found or query failed.`, { code });
73-
}
74-
await handleUpsert<CMAttributesResult>(connection, schema, 'cmattributes', { CoreMeasurementID: coreMeasurementID, Code: code }, 'CMAID');
57+
// Handle CM Attributes Upsert
58+
if (codes) {
59+
const parsedCodes = codes
60+
.split(';')
61+
.map(code => code.trim())
62+
.filter(Boolean);
63+
if (parsedCodes.length === 0) {
64+
console.error('No valid attribute codes found:', codes);
65+
} else {
66+
for (const code of parsedCodes) {
67+
const attributeRows = await runQuery(connection, `SELECT COUNT(*) as count FROM ${schema}.attributes WHERE Code = ?`, [code]);
68+
if (!attributeRows || attributeRows.length === 0 || !attributeRows[0].count) {
69+
throw createError(`Attribute code ${code} not found or query failed.`, { code });
7570
}
71+
await handleUpsert<CMAttributesResult>(connection, schema, 'cmattributes', { CoreMeasurementID: coreMeasurementID, Code: code }, 'CMAID');
7672
}
7773
}
74+
}
7875

79-
// Update Census Start/End Dates
80-
const combinedQuery = `
76+
// Update Census Start/End Dates
77+
const combinedQuery = `
8178
UPDATE ${schema}.census c
8279
JOIN (
8380
SELECT CensusID, MIN(MeasurementDate) AS FirstMeasurementDate, MAX(MeasurementDate) AS LastMeasurementDate
@@ -88,11 +85,10 @@ export async function processCensus(props: Readonly<SpecialProcessingProps>): Pr
8885
SET c.StartDate = m.FirstMeasurementDate, c.EndDate = m.LastMeasurementDate
8986
WHERE c.CensusID = ${censusID};`;
9087

91-
await runQuery(connection, combinedQuery);
92-
await connection.commit();
93-
console.log('Upsert successful. CoreMeasurement ID generated:', coreMeasurementID);
94-
return coreMeasurementID;
95-
}
88+
await runQuery(connection, combinedQuery);
89+
await connection.commit();
90+
console.log('Upsert successful. CoreMeasurement ID generated:', coreMeasurementID);
91+
return coreMeasurementID;
9692
}
9793
}
9894
} catch (error: any) {

frontend/components/processors/processorhelperfunctions.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export async function insertOrUpdate(props: InsertUpdateProcessingProps): Promis
2626
if (columns.includes('censusID')) rowData['censusID'] = subProps.censusID?.toString() ?? null;
2727
const tableColumns = columns.map(fileColumn => mapping.columnMappings[fileColumn]).join(', ');
2828
const placeholders = columns.map(() => '?').join(', '); // Use '?' for placeholders in MySQL
29-
const values = columns.map(fileColumn => rowData[fileColumn]);
29+
const values = columns.map(fileColumn => {
30+
const value = rowData[fileColumn];
31+
if (typeof value === 'string' && value === '') return null;
32+
return value;
33+
});
3034
const query = `
3135
INSERT INTO ${schema}.${mapping.tableName} (${tableColumns})
3236
VALUES (${placeholders}) ON DUPLICATE KEY

frontend/components/processors/processormacros.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ const sqlConfig: PoolOptions = {
104104
port: parseInt(process.env.AZURE_SQL_PORT!),
105105
database: process.env.AZURE_SQL_CATALOG_SCHEMA,
106106
waitForConnections: true,
107-
connectionLimit: 100, // increased from 10 to prevent bottlenecks
108-
queueLimit: 0,
107+
connectionLimit: 150, // increased from 10 to prevent bottlenecks
108+
queueLimit: 20,
109109
keepAliveInitialDelay: 10000, // 0 by default.
110110
enableKeepAlive: true, // false by default.
111111
connectTimeout: 20000 // 10 seconds by default.

0 commit comments

Comments
 (0)