Skip to content

Commit

Permalink
Merge branch 'development-hotfixes' into forestgeo-app-development
Browse files Browse the repository at this point in the history
  • Loading branch information
siddheshraze committed Jan 8, 2025
2 parents 5a83256 + c95d3b9 commit a8bec81
Show file tree
Hide file tree
Showing 12 changed files with 10,095 additions and 10,044 deletions.
8 changes: 0 additions & 8 deletions frontend/app/(hub)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -410,16 +410,8 @@ export default function HubLayout({ children }: { children: React.ReactNode }) {
justifyContent: 'center'
}}
>
{/*{siteConfig.name}*/}
<AcaciaVersionTypography>{siteConfig.name}</AcaciaVersionTypography>
</Typography>
{/*<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'center' }}>*/}
{/* <Tooltip title="Version" variant="solid" placement="top" arrow sx={{ pointerEvents: 'none' }}>*/}
{/* <Box sx={{ display: 'inline-block', verticalAlign: 'middle' }}>*/}
{/* <AcaciaVersionTypography>{siteConfig.version}</AcaciaVersionTypography>*/}
{/* </Box>*/}
{/* </Tooltip>*/}
{/*</Stack>*/}
</Stack>
<IconButton
onClick={() => setIsFeedbackModalOpen(true)}
Expand Down
10 changes: 5 additions & 5 deletions frontend/app/api/validations/crud/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function GET(_request: NextRequest) {
}

export async function POST(request: NextRequest) {
const { validationProcedure }: { validationProcedure: ValidationProceduresRDS } = await request.json();
const validationProcedure: ValidationProceduresRDS = await request.json();
const connectionManager = ConnectionManager.getInstance();
try {
delete validationProcedure['validationID'];
Expand All @@ -38,13 +38,13 @@ export async function POST(request: NextRequest) {
}

export async function PATCH(request: NextRequest) {
const { validationProcedure }: { validationProcedure: ValidationProceduresRDS } = await request.json();
const validationProcedure: ValidationProceduresRDS = await request.json();
const connectionManager = ConnectionManager.getInstance();
try {
const updatedValidationProcedure = delete validationProcedure['validationID'];
delete validationProcedure['id'];
const updateQuery = format('UPDATE ?? SET ? WHERE ValidationID = ?', [
`catalog.validationprocedures`,
updatedValidationProcedure,
validationProcedure,
validationProcedure.validationID
]);
await connectionManager.executeQuery(updateQuery);
Expand All @@ -59,7 +59,7 @@ export async function PATCH(request: NextRequest) {
}

export async function DELETE(request: NextRequest) {
const { validationProcedure }: { validationProcedure: ValidationProceduresRDS } = await request.json();
const validationProcedure: ValidationProceduresRDS = await request.json();
const connectionManager = ConnectionManager.getInstance();
try {
const deleteQuery = format('DELETE FROM ?? WHERE ValidationID = ?', [`catalog.validationprocedures`, validationProcedure.validationID]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ export async function POST(request: NextRequest, { params }: { params: { validat
try {
if (!params.validationType) throw new Error('validationProcedureName not provided');
const body = await request.json();
const { schema, validationProcedureID, cursorQuery, p_CensusID, p_PlotID, minDBH, maxDBH, minHOM, maxHOM } = body;
const { schema, validationProcedureID, cursorQuery, p_CensusID, p_PlotID, minDBH, maxDBH } = body;

// Execute the validation procedure using the provided inputs
const validationResponse = await runValidation(validationProcedureID, params.validationType, schema, cursorQuery, {
p_CensusID,
p_PlotID,
minDBH,
maxDBH,
minHOM,
maxHOM
maxDBH
});

return new NextResponse(JSON.stringify(validationResponse), {
Expand Down
1 change: 1 addition & 0 deletions frontend/components/client/specieslimitsmodal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export default function SpeciesLimitsModal(props: {
try {
const submittedSpeciesLimit: SpeciesLimitsRDS = {
...newSpeciesLimit,
limitType: 'DBH',
censusID: currentCensus?.dateRanges[0].censusID,
plotID: currentPlot?.plotID,
speciesLimitID: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'use client';
import { GridColDef, GridRenderEditCellParams } from '@mui/x-data-grid';
import React, { useEffect, useState } from 'react';
import { Box, Button, Tooltip, Typography } from '@mui/joy';
import { Box, Button } from '@mui/joy';
import UploadParentModal from '@/components/uploadsystemhelpers/uploadparentmodal';
import { FormType } from '@/config/macros/formdetails';
import { AllTaxonomiesViewRDS } from '@/config/sqlrdsdefinitions/views';
Expand Down
35 changes: 19 additions & 16 deletions frontend/components/datagrids/measurementscommons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,29 @@ import {
ToolbarPropsOverrides,
useGridApiRef
} from '@mui/x-data-grid';
import { Alert, AlertColor, AlertProps, AlertPropsColorOverrides, Button, Checkbox, IconButton, Snackbar } from '@mui/material';
import { Alert, AlertColor, AlertProps, AlertPropsColorOverrides, Checkbox, Snackbar } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/DeleteOutlined';
import SaveIcon from '@mui/icons-material/Save';
import CancelIcon from '@mui/icons-material/Close';
import RefreshIcon from '@mui/icons-material/Refresh';
import Box from '@mui/joy/Box';
import { DialogActions, DialogContent, DialogTitle, Dropdown, Menu, MenuButton, MenuItem, Modal, ModalDialog, Stack, Tooltip, Typography } from '@mui/joy';
import {
Button,
DialogActions,
DialogContent,
DialogTitle,
Dropdown,
IconButton,
Menu,
MenuButton,
MenuItem,
Modal,
ModalDialog,
Stack,
Tooltip,
Typography
} from '@mui/joy';
import { StyledDataGrid } from '@/config/styleddatagrid';
import {
CellItemContainer,
Expand All @@ -53,7 +68,6 @@ import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty';
import { HTTPResponses } from '@/config/macros';
import { useLoading } from '@/app/contexts/loadingprovider';
import { useSession } from 'next-auth/react';

import ConfirmationDialog from './confirmationdialog';
import ReEnterDataModal from './reentrydatamodal';
import { FormType, getTableHeaders } from '@/config/macros/formdetails';
Expand Down Expand Up @@ -89,7 +103,6 @@ const EditToolbar = (props: EditToolbarProps) => {
handleExportAll,
handleExportCSV,
handleQuickFilterChange,
locked,
filterModel,
dynamicButtons = []
} = props;
Expand Down Expand Up @@ -190,23 +203,13 @@ const EditToolbar = (props: EditToolbarProps) => {
}}
/>
<Tooltip title={'Clear filter'} placement={'right'}>
<IconButton
aria-label={'clear filter'}
disabled={inputValue === ''}
onClick={handleClearInput}
size={'small'}
edge={'end'}
sx={{ marginLeft: 1 }}
>
<IconButton aria-label={'clear filter'} disabled={inputValue === ''} onClick={handleClearInput} size={'sm'} sx={{ marginLeft: 1 }}>
<ClearIcon fontSize={'small'} />
</IconButton>
</Tooltip>
</Box>
</Tooltip>
{/*<Button variant={'text'} color={'primary'} startIcon={<AddIcon />} onClick={async () => await handleAddNewRow()} disabled={locked}>*/}
{/* Add Row*/}
{/*</Button>*/}
<Button variant={'text'} color={'primary'} startIcon={<RefreshIcon />} onClick={async () => await handleRefresh()}>
<Button color={'primary'} variant={'plain'} startDecorator={<RefreshIcon />} onClick={async () => await handleRefresh()}>
Refresh
</Button>
<Dropdown>
Expand Down
32 changes: 13 additions & 19 deletions frontend/components/processors/processorhelperfunctions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,6 @@ export async function runValidation(
p_PlotID?: number | null;
minDBH?: number | null;
maxDBH?: number | null;
minHOM?: number | null;
maxHOM?: number | null;
} = {}
): Promise<boolean> {
const connectionManager = ConnectionManager.getInstance();
Expand All @@ -287,10 +285,8 @@ export async function runValidation(
const formattedCursorQuery = cursorQuery
.replace(/@p_CensusID/g, params.p_CensusID !== null && params.p_CensusID !== undefined ? params.p_CensusID.toString() : 'NULL')
.replace(/@p_PlotID/g, params.p_PlotID !== null && params.p_PlotID !== undefined ? params.p_PlotID.toString() : 'NULL')
.replace(/@minDBH/g, params.minDBH !== null && params.minDBH !== undefined ? params.minDBH.toString() : 'NULL')
.replace(/@maxDBH/g, params.maxDBH !== null && params.maxDBH !== undefined ? params.maxDBH.toString() : 'NULL')
.replace(/@minHOM/g, params.minHOM !== null && params.minHOM !== undefined ? params.minHOM.toString() : 'NULL')
.replace(/@maxHOM/g, params.maxHOM !== null && params.maxHOM !== undefined ? params.maxHOM.toString() : 'NULL')
// .replace(/@minDBH/g, params.minDBH !== null && params.minDBH !== undefined ? params.minDBH.toString() : 'NULL')
// .replace(/@maxDBH/g, params.maxDBH !== null && params.maxDBH !== undefined ? params.maxDBH.toString() : 'NULL')
.replace(/@validationProcedureID/g, validationProcedureID.toString())
.replace(/cmattributes/g, 'TEMP_CMATTRIBUTES_PLACEHOLDER')
.replace(/coremeasurements/g, `${schema}.coremeasurements`)
Expand All @@ -308,13 +304,11 @@ export async function runValidation(
.replace(/TEMP_CMATTRIBUTES_PLACEHOLDER/g, `${schema}.cmattributes`);

// Advanced handling: If minDBH, maxDBH, minHOM, or maxHOM are null, dynamically fetch the species-specific limits.
if (params.minDBH === null || params.maxDBH === null || params.minHOM === null || params.maxHOM === null) {
if (params.minDBH === null || params.maxDBH === null) {
const speciesLimitsQuery = `
SELECT sl.LimitType,
COALESCE(${params.minDBH !== null && params.minDBH !== undefined ? params.minDBH.toString() : 'NULL'}, IF(sl.LimitType = 'DBH', sl.LowerBound, NULL)) AS minDBH,
COALESCE(${params.maxDBH !== null && params.maxDBH !== undefined ? params.maxDBH.toString() : 'NULL'}, IF(sl.LimitType = 'DBH', sl.UpperBound, NULL)) AS maxDBH,
COALESCE(${params.minHOM !== null && params.minHOM !== undefined ? params.minHOM.toString() : 'NULL'}, IF(sl.LimitType = 'HOM', sl.LowerBound, NULL)) AS minHOM,
COALESCE(${params.maxHOM !== null && params.maxHOM !== undefined ? params.maxHOM.toString() : 'NULL'}, IF(sl.LimitType = 'HOM', sl.UpperBound, NULL)) AS maxHOM
SELECT sp.SpeciesID, sl.LimitType,
IF(sl.LimitType = 'DBH', sl.LowerBound, NULL) AS minDBH,
IF(sl.LimitType = 'DBH', sl.UpperBound, NULL) AS maxDBH
FROM ${schema}.specieslimits sl
JOIN
${schema}.species sp ON sp.SpeciesID = sl.SpeciesID
Expand All @@ -327,27 +321,27 @@ export async function runValidation(
JOIN
${schema}.coremeasurements cm ON cm.StemID = st.StemID
WHERE cm.IsValidated IS NULL
AND (${params.p_CensusID !== null ? `cm.CensusID = ${params.p_CensusID}` : 'TRUE'})
AND (${params.p_PlotID !== null ? `q.PlotID = ${params.p_PlotID}` : 'TRUE'})
AND (${params.p_CensusID !== null ? `sl.CensusID = ${params.p_CensusID}` : '1 = 1'})
AND (${params.p_PlotID !== null ? `sl.PlotID = ${params.p_PlotID}` : '1 = 1'})
LIMIT 1;
`;
console.log('completed speciesLimits query: ', speciesLimitsQuery);
const speciesLimits = await connectionManager.executeQuery(speciesLimitsQuery);
console.log('RESULTS: specieslimits query: ', speciesLimits);

if (speciesLimits.length > 0) {
// If any species-specific limits were fetched, update the variables
params.minDBH = speciesLimits[0].minDBH || params.minDBH;
params.maxDBH = speciesLimits[0].maxDBH || params.maxDBH;
params.minHOM = speciesLimits[0].minHOM || params.minHOM;
params.maxHOM = speciesLimits[0].maxHOM || params.maxHOM;
}
}

console.log('updated params? ', params);

// Reformat the query after potentially updating the parameters with species-specific limits
const reformattedCursorQuery = formattedCursorQuery
.replace(/@minDBH/g, params.minDBH !== null && params.minDBH !== undefined ? params.minDBH.toString() : 'NULL')
.replace(/@maxDBH/g, params.maxDBH !== null && params.maxDBH !== undefined ? params.maxDBH.toString() : 'NULL')
.replace(/@minHOM/g, params.minHOM !== null && params.minHOM !== undefined ? params.minHOM.toString() : 'NULL')
.replace(/@maxHOM/g, params.maxHOM !== null && params.maxHOM !== undefined ? params.maxHOM.toString() : 'NULL');
.replace(/@maxDBH/g, params.maxDBH !== null && params.maxDBH !== undefined ? params.maxDBH.toString() : 'NULL');

// Execute the cursor query to get the rows that need validation
console.log('running validation: ', validationProcedureName);
Expand Down
40 changes: 25 additions & 15 deletions frontend/components/processors/processormacros.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PoolConnection, PoolOptions } from 'mysql2/promise';
import { PoolMonitor } from '@/config/poolmonitor';
import chalk from 'chalk';

const sqlConfig: PoolOptions = {
user: process.env.AZURE_SQL_USER,
Expand All @@ -8,8 +9,8 @@ const sqlConfig: PoolOptions = {
port: parseInt(process.env.AZURE_SQL_PORT!),
database: process.env.AZURE_SQL_CATALOG_SCHEMA,
waitForConnections: true,
connectionLimit: 100, // increased from 10 to prevent bottlenecks
queueLimit: 20,
connectionLimit: 50, // Lower limit if 100 is excessive for your DB
queueLimit: 5, // Smaller queue to fail fast on overload
keepAliveInitialDelay: 10000, // 0 by default.
enableKeepAlive: true, // false by default.
connectTimeout: 20000 // 10 seconds by default.
Expand All @@ -20,19 +21,20 @@ export async function getSqlConnection(tries: number): Promise<PoolConnection> {
try {
console.log(`Attempting to get SQL connection. Try number: ${tries + 1}`);

// Check if the pool is closed and reinitialize if necessary
if (poolMonitor.isPoolClosed()) {
console.log('Connection pool is closed. Reinitializing...');
await poolMonitor.reinitializePool();
}

// Acquire the connection and ping to validate it
const connection = await poolMonitor.getConnection();
await connection.ping(); // Use ping to check the connection
console.log('Connection successful');
return connection; // Resolve the connection when successful
} catch (err) {
console.error(`Connection attempt ${tries + 1} failed:`, err);
if (tries == 5) {

if (tries === 5) {
console.error('!!! Cannot connect !!! Error:', err);
throw err;
} else {
Expand All @@ -59,29 +61,37 @@ export async function getConn() {
}

export async function runQuery(connection: PoolConnection, query: string, params?: any[]): Promise<any> {
const timeout = 10000; // 10 seconds
const timer = new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Query execution timed out')), timeout));
const startTime = Date.now();
try {
// If params exist, replace any undefined values with null
if (params) {
params = params.map(param => (param === undefined ? null : param));
}

// Check if the query is for calling a stored procedure
// Log query details
console.log(chalk.cyan(`Executing query: ${query}`));
if (params) {
console.log(chalk.gray(`Query params: ${JSON.stringify(params)}`));
}
if (query.trim().startsWith('CALL')) {
// Use `connection.query` for stored procedures
const [rows] = await connection.query(query, params);
const [rows] = await Promise.race([connection.query(query, params), timer]);
console.log(chalk.magenta(`CALL Query completed in ${Date.now() - startTime}ms`));
return rows;
} else {
// Use `connection.execute` for standard SQL queries
const [rows, _fields] = await connection.execute(query, params);
const [rows, _fields] = await Promise.race([connection.execute(query, params), timer]);
console.log(chalk.magenta(`STANDARD Query completed in ${Date.now() - startTime}ms`));

// Check if the query is an INSERT, UPDATE, or DELETE
if (query.trim().startsWith('INSERT') || query.trim().startsWith('UPDATE') || query.trim().startsWith('DELETE')) {
return rows; // This will include insertId, affectedRows, etc.
return rows;
}
return rows; // This is for SELECT queries and will return RowDataPacket[]
return rows;
}
} catch (error: any) {
console.error('Error executing query:', error.message);
console.error(chalk.red(`Error executing query: ${query}`));
if (params) {
console.error(chalk.red(`With params: ${JSON.stringify(params)}`));
}
console.error(chalk.red('Error message:', error.message));
throw error;
}
}
Expand Down
2 changes: 0 additions & 2 deletions frontend/components/validationrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Chip, IconButton, List, ListItem, Switch, Textarea, Tooltip } from '@mu
import dynamic from 'next/dynamic';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { useSiteContext } from '@/app/contexts/userselectionprovider';

function useDebouncedCallback(callback: (...args: any[]) => void, delay: number) {
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
Expand Down Expand Up @@ -43,7 +42,6 @@ const ValidationRow: React.FC<ValidationRowProps> = ({
const debouncedSetScriptContent = useDebouncedCallback(value => setScriptContent(value ?? ''), 300);
const [isEditing, setIsEditing] = useState(false);
const originalScriptContent = useRef<string>(validation.definition ?? '');
const currentSite = useSiteContext();

const CustomMonacoEditor = dynamic(() => import('@/components/client/custommonacoeditor'), { ssr: false });

Expand Down
Loading

0 comments on commit a8bec81

Please sign in to comment.