Skip to content

Commit

Permalink
isolated implementation has been resolved to address all supporting d…
Browse files Browse the repository at this point in the history
…ata fields' manual entry
  • Loading branch information
siddheshraze committed Sep 13, 2024
1 parent 98c25b9 commit aef80d8
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 256 deletions.
127 changes: 78 additions & 49 deletions frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { format, PoolConnection } from 'mysql2/promise';
import { NextRequest, NextResponse } from 'next/server';
import {
AllTaxonomiesViewQueryConfig,
generateInsertOperations,
generateUpdateOperations,
handleDeleteForSlices,
handleUpsertForSlices,
StemTaxonomiesViewQueryConfig
} from '@/components/processors/processorhelperfunctions';
import { HTTPResponses } from '@/config/macros';
import { HTTPResponses } from '@/config/macros'; // slugs SHOULD CONTAIN AT MINIMUM: schema, page, pageSize, plotID, plotCensusNumber, (optional) quadratID, (optional) speciesID

// slugs SHOULD CONTAIN AT MINIMUM: schema, page, pageSize, plotID, plotCensusNumber, (optional) quadratID, (optional) speciesID
export async function GET(
Expand Down Expand Up @@ -223,16 +223,21 @@ export async function POST(request: NextRequest, { params }: { params: { dataTyp
if (!params.slugs) throw new Error('slugs not provided');
const [schema, gridID] = params.slugs;
if (!schema || !gridID) throw new Error('no schema or gridID provided');

let conn: PoolConnection | null = null;
const { newRow } = await request.json();
let insertID: number | undefined = undefined;
let insertIDs: { [key: string]: number } = {};

try {
conn = await getConn();
await conn.beginTransaction();

if (Object.keys(newRow).includes('isNew')) delete newRow.isNew;

const newRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const demappedGridID = gridID.charAt(0).toUpperCase() + gridID.substring(1);

// Handle SQL views with handleUpsertForSlices
if (params.dataType.includes('view')) {
let queryConfig;
switch (params.dataType) {
Expand All @@ -243,25 +248,32 @@ export async function POST(request: NextRequest, { params }: { params: { dataTyp
queryConfig = StemTaxonomiesViewQueryConfig;
break;
default:
throw new Error('incorrect view call');
throw new Error('Incorrect view call');
}
const insertQueries = generateInsertOperations(schema, newRow, queryConfig);
for (const query of insertQueries) {
await runQuery(conn, query);
}
} else if (params.dataType === 'attributes') {

// Use handleUpsertForSlices and retrieve the insert IDs
insertIDs = await handleUpsertForSlices(conn, schema, newRowData, queryConfig);
}

// Handle the case for 'attributes'
else if (params.dataType === 'attributes') {
const insertQuery = format('INSERT INTO ?? SET ?', [`${schema}.${params.dataType}`, newRowData]);
const results = await runQuery(conn, insertQuery);
insertID = results.insertId;
} else {
insertIDs = { attributes: results.insertId }; // Standardize output with table name as key
}

// Handle all other cases
else {
delete newRowData[demappedGridID];
if (params.dataType === 'plots') delete newRowData.NumQuadrats;
const insertQuery = format('INSERT INTO ?? SET ?', [`${schema}.${params.dataType}`, newRowData]);
const results = await runQuery(conn, insertQuery);
insertID = results.insertId;
insertIDs = { [params.dataType]: results.insertId }; // Standardize output with table name as key
}

// Commit the transaction and return the standardized response
await conn.commit();
return NextResponse.json({ message: 'Insert successful', createdID: insertID }, { status: HTTPResponses.OK });
return NextResponse.json({ message: 'Insert successful', createdIDs: insertIDs }, { status: HTTPResponses.OK });
} catch (error: any) {
return handleError(error, conn, newRow);
} finally {
Expand All @@ -274,24 +286,18 @@ export async function PATCH(request: NextRequest, { params }: { params: { dataTy
if (!params.slugs) throw new Error('slugs not provided');
const [schema, gridID] = params.slugs;
if (!schema || !gridID) throw new Error('no schema or gridID provided');

let conn: PoolConnection | null = null;
const demappedGridID = gridID.charAt(0).toUpperCase() + gridID.substring(1);
const { newRow, oldRow } = await request.json();
let updateIDs: { [key: string]: number } = {};

try {
conn = await getConn();
await conn.beginTransaction();
if (!['alltaxonomiesview', 'stemtaxonomiesview', 'measurementssummaryview'].includes(params.dataType)) {
const newRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const { [demappedGridID]: gridIDKey, ...remainingProperties } = newRowData;
const updateQuery = format(
`UPDATE ??
SET ?
WHERE ?? = ?`,
[`${schema}.${params.dataType}`, remainingProperties, demappedGridID, gridIDKey]
);
await runQuery(conn, updateQuery);
await conn.commit();
} else {

// Handle views with handleUpsertForSlices (applies to both insert and update logic)
if (['alltaxonomiesview', 'stemtaxonomiesview'].includes(params.dataType)) {
let queryConfig;
switch (params.dataType) {
case 'alltaxonomiesview':
Expand All @@ -301,32 +307,45 @@ export async function PATCH(request: NextRequest, { params }: { params: { dataTy
queryConfig = StemTaxonomiesViewQueryConfig;
break;
default:
throw new Error('incorrect view call');
}
const updateQueries = generateUpdateOperations(schema, newRow, oldRow, queryConfig);
for (const query of updateQueries) {
await runQuery(conn, query);
throw new Error('Incorrect view call');
}
await conn.commit();

// Use handleUpsertForSlices for update operations as well (updates where needed)
updateIDs = await handleUpsertForSlices(conn, schema, newRow, queryConfig);
}

// Handle non-view table updates
else {
const newRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const { [demappedGridID]: gridIDKey, ...remainingProperties } = newRowData;

// Construct the UPDATE query
const updateQuery = format(
`UPDATE ??
SET ?
WHERE ?? = ?`,
[`${schema}.${params.dataType}`, remainingProperties, demappedGridID, gridIDKey]
);

// Execute the UPDATE query
await runQuery(conn, updateQuery);

// For non-view tables, standardize the response format
updateIDs = { [params.dataType]: gridIDKey };
}
return NextResponse.json({ message: 'Update successful' }, { status: HTTPResponses.OK });

// Commit the transaction
await conn.commit();

// Return a standardized response with updated IDs
return NextResponse.json({ message: 'Update successful', updatedIDs: updateIDs }, { status: HTTPResponses.OK });
} catch (error: any) {
return handleError(error, conn, newRow);
} finally {
if (conn) conn.release();
}
}

// Define mappings for views to base tables and primary keys
const viewToTableMappings: Record<string, { table: string; primaryKey: string }> = {
alltaxonomiesview: { table: 'species', primaryKey: 'SpeciesID' },
stemtaxonomiesview: { table: 'stems', primaryKey: 'StemID' },
measurementssummaryview: {
table: 'coremeasurements',
primaryKey: 'CoreMeasurementID'
}
};

// slugs: schema, gridID
// body: full data row, only need first item from it this time though
export async function DELETE(request: NextRequest, { params }: { params: { dataType: string; slugs?: string[] } }) {
Expand All @@ -344,17 +363,27 @@ export async function DELETE(request: NextRequest, { params }: { params: { dataT
// Handle deletion for views
if (['alltaxonomiesview', 'stemtaxonomiesview', 'measurementssummaryview'].includes(params.dataType)) {
const deleteRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const viewConfig = viewToTableMappings[params.dataType];
if (!viewConfig) throw new Error(`No table mapping found for view ${params.dataType}`);

const { [viewConfig.primaryKey]: primaryKeyValue } = deleteRowData;
if (!primaryKeyValue) throw new Error(`Primary key value missing for ${viewConfig.primaryKey} in view ${params.dataType}`);
// Prepare query configuration based on view
let queryConfig;
switch (params.dataType) {
case 'alltaxonomiesview':
queryConfig = AllTaxonomiesViewQueryConfig;
break;
case 'stemtaxonomiesview':
queryConfig = StemTaxonomiesViewQueryConfig;
break;
default:
throw new Error('Incorrect view call');
}

// Use handleDeleteForSlices for handling deletion, taking foreign key constraints into account
await handleDeleteForSlices(conn, schema, deleteRowData, queryConfig);

const deleteQuery = format(`DELETE FROM ?? WHERE ?? = ?`, [`${schema}.${params.dataType}`, viewConfig.primaryKey, primaryKeyValue]);
await runQuery(conn, deleteQuery);
await conn.commit();
return NextResponse.json({ message: 'Delete successful' }, { status: HTTPResponses.OK });
}

// Handle deletion for tables
const deleteRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const { [demappedGridID]: gridIDKey } = deleteRowData;
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/client/datagridcolumns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1029,7 +1029,7 @@ export const SpeciesGridColumns: GridColDef[] = [
editable: true
},
{
field: 'subSpeciesName',
field: 'subspeciesName',
headerName: 'Subspecies',
headerClassName: 'header',
flex: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function IsolatedAllTaxonomiesViewDataGrid() {
speciesIDLevel: '',
speciesAuthority: '',
subspeciesAuthority: '',
validCode: '',
fieldFamily: '',
speciesDescription: ''
};
Expand Down Expand Up @@ -172,6 +173,7 @@ export default function IsolatedAllTaxonomiesViewDataGrid() {
field: 'speciesName',
headerName: 'Species',
headerClassName: 'header',
renderHeader: () => formatHeader('Species', 'Name'),
flex: 1,
align: 'center',
headerAlign: 'center',
Expand All @@ -182,6 +184,7 @@ export default function IsolatedAllTaxonomiesViewDataGrid() {
field: 'subspeciesName',
headerName: 'Subspecies',
headerClassName: 'header',
renderHeader: () => formatHeader('Subspecies', 'Name'),
flex: 1,
align: 'center',
headerAlign: 'center',
Expand Down Expand Up @@ -232,6 +235,17 @@ export default function IsolatedAllTaxonomiesViewDataGrid() {
type: 'string',
editable: true
},
{
field: 'validCode',
headerName: 'Valid Code',
renderHeader: () => formatHeader('Valid', 'Code'),
headerClassName: 'header',
flex: 1,
align: 'center',
headerAlign: 'center',
type: 'string',
editable: true
},
{
field: 'speciesDescription',
headerName: 'Species Description',
Expand Down Expand Up @@ -303,7 +317,7 @@ export default function IsolatedAllTaxonomiesViewDataGrid() {
clusters={{
Family: ['family'],
Genus: ['genus', 'genusAuthority'],
Species: ['speciesCode', 'speciesName', 'speciesIDLevel', 'speciesAuthority', 'fieldFamily', 'speciesDescription'],
Species: ['speciesCode', 'speciesName', 'speciesIDLevel', 'speciesAuthority', 'fieldFamily', 'validCode', 'speciesDescription'],
Subspecies: ['subspeciesName', 'subspeciesAuthority']
}}
/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/datagrids/isolateddatagridcommons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ export default function IsolatedDataGridCommons(props: Readonly<IsolatedDataGrid
if (locked || !promiseArguments) return;

setLoading(true, 'Saving changes...');

try {
// Set the row to view mode after confirmation
setRowModesModel(prevModel => ({
Expand Down Expand Up @@ -876,6 +875,7 @@ export default function IsolatedDataGridCommons(props: Readonly<IsolatedDataGrid
)}
{isDialogOpen && promiseArguments && (
<ReEnterDataModal
gridType={gridType}
row={promiseArguments.oldRow} // Pass oldRow
reEnterData={promiseArguments.newRow} // Pass newRow
handleClose={handleCancelAction}
Expand Down
13 changes: 12 additions & 1 deletion frontend/components/datagrids/reentrydatamodal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { areaSelectionOptions, unitSelectionOptions } from '@/config/macros';
import { useOrgCensusContext, usePlotContext } from '@/app/contexts/userselectionprovider';

interface ReEnterDataModalProps {
gridType: string;
row: GridRowModel;
reEnterData: GridRowModel | null;
handleClose: () => void;
Expand All @@ -34,6 +35,7 @@ interface ReEnterDataModalProps {
}

const ReEnterDataModal: React.FC<ReEnterDataModalProps> = ({
gridType,
row,
reEnterData,
handleClose,
Expand Down Expand Up @@ -135,7 +137,16 @@ const ReEnterDataModal: React.FC<ReEnterDataModalProps> = ({
console.log('selectedRow: ', selectedRow);
const normalizedData = normalizeRowData(remaining);
console.log('normalized row: ', normalizedData);
handleSave(normalizedData);
if (gridType === 'alltaxonomiesview') {
// need to switch the speciesDescription to description:
const { speciesDescription, speciesIDLevel, ...rest } = normalizedData;
const updatedData = {
...rest,
description: speciesDescription,
idLevel: speciesIDLevel
};
handleSave(updatedData);
} else handleSave(normalizedData);
}
};

Expand Down
Loading

0 comments on commit aef80d8

Please sign in to comment.