Skip to content

Commit

Permalink
saving changes -- pushing to dev
Browse files Browse the repository at this point in the history
  • Loading branch information
siddheshraze committed Jul 24, 2024
1 parent 6c772ca commit efc9d5d
Show file tree
Hide file tree
Showing 18 changed files with 2,139 additions and 1,635 deletions.
4 changes: 2 additions & 2 deletions frontend/app/(hub)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default function HubLayout({ children }: { children: React.ReactNode }) {
setLoading(true, 'Converting raw census data...');
const censusList = await createAndUpdateCensusList(censusRDSLoad);
if (censusListDispatch) {
censusListDispatch({ censusList });
await censusListDispatch({censusList});
}
setLoading(false);
setCensusListLoaded(true);
Expand Down Expand Up @@ -322,7 +322,7 @@ export default function HubLayout({ children }: { children: React.ReactNode }) {
paddingBottom: '20px',
flexDirection: 'column',
}}>
{renderSwitch(usePathname())}
{renderSwitch(pathname)}
</Box>
<Divider orientation={"horizontal"} sx={{ my: '5px' }} />
<Box
Expand Down
7 changes: 7 additions & 0 deletions frontend/app/(hub)/measurementshub/viewfulltable/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use client";

import ViewFullTableViewDataGrid from "@/components/datagrids/applications/viewfulltableviewdatagrid";

export default function ViewFullTablePage() {
return <ViewFullTableViewDataGrid />;
}
76 changes: 44 additions & 32 deletions frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { getConn, runQuery } from "@/components/processors/processormacros";
import {getConn, runQuery} from "@/components/processors/processormacros";
import MapperFactory from "@/config/datamapper";
import { handleError } from "@/utils/errorhandler";
import { PoolConnection, format } from "mysql2/promise";
import { NextRequest, NextResponse } from "next/server";
import { generateInsertOperations, generateUpdateOperations, StemDimensionsViewQueryConfig, AllTaxonomiesViewQueryConfig, StemTaxonomiesViewQueryConfig } from '@/components/processors/processorhelperfunctions';
import { HTTPResponses } from "@/config/macros";
import {handleError} from "@/utils/errorhandler";
import {PoolConnection, format} from "mysql2/promise";
import {NextRequest, NextResponse} from "next/server";
import {
generateInsertOperations,
generateUpdateOperations,
StemDimensionsViewQueryConfig,
AllTaxonomiesViewQueryConfig,
StemTaxonomiesViewQueryConfig
} from '@/components/processors/processorhelperfunctions';
import {HTTPResponses} from "@/config/macros";

// slugs SHOULD CONTAIN AT MINIMUM: schema, page, pageSize, plotID, plotCensusNumber, (optional) quadratID
export async function GET(request: NextRequest, { params }: {
export async function GET(request: NextRequest, {params}: {
params: { dataType: string, slugs?: string[] }
}): Promise<NextResponse<{ output: any[], deprecated?: any[], totalCount: number }>> {
if (!params.slugs || params.slugs.length < 5) throw new Error("slugs not received.");
Expand Down Expand Up @@ -57,6 +63,8 @@ export async function GET(request: NextRequest, { params }: {
LIMIT ?, ?`;
queryParams.push(plotID, plotCensusNumber, page * pageSize, pageSize);
break;
case 'measurementssummaryview':
case 'viewfulltableview':
case 'quadrats':
paginatedQuery = `
SELECT SQL_CALC_FOUND_ROWS q.*
Expand All @@ -68,7 +76,6 @@ export async function GET(request: NextRequest, { params }: {
WHERE c.PlotID = ?
AND c.PlotCensusNumber = ?
)
GROUP BY q.QuadratID
LIMIT ?, ?`;
queryParams.push(plotID, plotID, plotCensusNumber, page * pageSize, pageSize);
break;
Expand Down Expand Up @@ -100,9 +107,6 @@ export async function GET(request: NextRequest, { params }: {
queryParams.push(plotID, page * pageSize, pageSize);
break;
case 'coremeasurements':
case 'measurementssummaryview':
case 'viewfulltableview':
case 'stemdimensionsview':
// Retrieve multiple past CensusID for the given PlotCensusNumber
const censusQuery = `
SELECT CensusID
Expand Down Expand Up @@ -155,9 +159,12 @@ export async function GET(request: NextRequest, { params }: {
}

const paginatedResults = await runQuery(conn, format(paginatedQuery, queryParams));
console.log('formatted query: ', format(paginatedQuery, queryParams));
console.log('paginated results: ', paginatedResults);

const totalRowsQuery = "SELECT FOUND_ROWS() as totalRows";
const totalRowsResult = await runQuery(conn, totalRowsQuery);
console.log('total rows results: ', totalRowsResult);
const totalRows = totalRowsResult[0].totalRows;

if (updatedMeasurementsExist) {
Expand All @@ -180,15 +187,15 @@ export async function GET(request: NextRequest, { params }: {
output: rows,
deprecated: deprecatedRows,
totalCount: totalRows
}), { status: HTTPResponses.OK });
}), {status: HTTPResponses.OK});
} else {
const mapper = MapperFactory.getMapper<any, any>(params.dataType);
const rows = mapper.mapData(paginatedResults);
return new NextResponse(JSON.stringify({
output: rows,
deprecated: undefined,
totalCount: totalRows
}), { status: HTTPResponses.OK });
}), {status: HTTPResponses.OK});
}
} catch (error: any) {
if (conn) await conn.rollback();
Expand All @@ -199,12 +206,12 @@ export async function GET(request: NextRequest, { params }: {
}

// required dynamic parameters: dataType (fixed),[ schema, gridID value] -> slugs
export async function POST(request: NextRequest, { params }: { params: { dataType: string, slugs?: string[] } }) {
export async function POST(request: NextRequest, {params}: { params: { dataType: string, slugs?: string[] } }) {
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();
const {newRow} = await request.json();
let insertID: number | undefined = undefined;
try {
conn = await getConn();
Expand Down Expand Up @@ -245,7 +252,7 @@ export async function POST(request: NextRequest, { params }: { params: { dataTyp
insertID = results.insertId;
}
await conn.commit();
return NextResponse.json({ message: "Insert successful", createdID: insertID}, { status: HTTPResponses.OK });
return NextResponse.json({message: "Insert successful", createdID: insertID}, {status: HTTPResponses.OK});
} catch (error: any) {
return handleError(error, conn, newRow);
} finally {
Expand All @@ -254,21 +261,23 @@ export async function POST(request: NextRequest, { params }: { params: { dataTyp
}

// slugs: schema, gridID
export async function PATCH(request: NextRequest, { params }: { params: { dataType: string, slugs?: string[] } }) {
export async function PATCH(request: NextRequest, {params}: { params: { dataType: string, slugs?: string[] } }) {
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();
const {newRow, oldRow} = await request.json();
try {
conn = await getConn();
await conn.beginTransaction();
if (!['alltaxonomiesview', 'stemdimensionsview', 'stemtaxonomiesview', 'measurementssummaryview'].includes(params.dataType)) {
const mapper = MapperFactory.getMapper<any, any>(params.dataType);
const newRowData = mapper.demapData([newRow])[0];
const { [demappedGridID]: gridIDKey, ...remainingProperties } = newRowData;
const updateQuery = format(`UPDATE ?? SET ? WHERE ?? = ?`, [`${schema}.${params.dataType}`, remainingProperties, demappedGridID, gridIDKey]);
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 {
Expand All @@ -292,7 +301,7 @@ export async function PATCH(request: NextRequest, { params }: { params: { dataTy
}
await conn.commit();
}
return NextResponse.json({ message: "Update successful" }, { status: HTTPResponses.OK });
return NextResponse.json({message: "Update successful"}, {status: HTTPResponses.OK});
} catch (error: any) {
return handleError(error, conn, newRow);
} finally {
Expand All @@ -302,21 +311,21 @@ export async function PATCH(request: NextRequest, { params }: { params: { dataTy

// Define mappings for views to base tables and primary keys
const viewToTableMappings: Record<string, { table: string, primaryKey: string }> = {
'alltaxonomiesview': { table: 'species', primaryKey: 'SpeciesID' },
'stemdimensionsview': { table: 'stems', primaryKey: 'StemID' },
'stemtaxonomiesview': { table: 'stems', primaryKey: 'StemID' },
'measurementssummaryview': { table: 'coremeasurements', primaryKey: 'CoreMeasurementID' },
'alltaxonomiesview': {table: 'species', primaryKey: 'SpeciesID'},
'stemdimensionsview': {table: 'stems', primaryKey: 'StemID'},
'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[] } }) {
export async function DELETE(request: NextRequest, {params}: { params: { dataType: string, slugs?: string[] } }) {
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 } = await request.json();
const {newRow} = await request.json();
try {
conn = await getConn();
await conn.beginTransaction();
Expand All @@ -328,27 +337,30 @@ export async function DELETE(request: NextRequest, { params }: { params: { dataT
const viewConfig = viewToTableMappings[params.dataType];
if (!viewConfig) throw new Error(`No table mapping found for view ${params.dataType}`);

const { [viewConfig.primaryKey]: primaryKeyValue } = deleteRowData;
const {[viewConfig.primaryKey]: primaryKeyValue} = deleteRowData;
if (!primaryKeyValue) throw new Error(`Primary key value missing for ${viewConfig.primaryKey} in view ${params.dataType}`);

const deleteQuery = format(`DELETE FROM ?? WHERE ?? = ?`, [`${schema}.${viewConfig.table}`, viewConfig.primaryKey, primaryKeyValue]);
await runQuery(conn, deleteQuery);
await conn.commit();
return NextResponse.json({ message: "Delete successful" }, { status: HTTPResponses.OK });
return NextResponse.json({message: "Delete successful"}, {status: HTTPResponses.OK});
}
// Handle deletion for tables
const mapper = MapperFactory.getMapper<any, any>(params.dataType);
const deleteRowData = mapper.demapData([newRow])[0];
const { [demappedGridID]: gridIDKey, ...remainingProperties } = deleteRowData;
const {[demappedGridID]: gridIDKey, ...remainingProperties} = deleteRowData;
const deleteQuery = format(`DELETE FROM ?? WHERE ?? = ?`, [`${schema}.${params.dataType}`, demappedGridID, gridIDKey]);
await runQuery(conn, deleteQuery);
await conn.commit();
return NextResponse.json({ message: "Delete successful" }, { status: HTTPResponses.OK });
return NextResponse.json({message: "Delete successful"}, {status: HTTPResponses.OK});
} catch (error: any) {
if (error.code === 'ER_ROW_IS_REFERENCED_2') {
const referencingTableMatch = error.message.match(/CONSTRAINT `(.*?)` FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)`/);
const referencingTable = referencingTableMatch ? referencingTableMatch[3] : 'unknown';
return NextResponse.json({ message: "Foreign key conflict detected", referencingTable }, { status: HTTPResponses.FOREIGN_KEY_CONFLICT });
return NextResponse.json({
message: "Foreign key conflict detected",
referencingTable
}, {status: HTTPResponses.FOREIGN_KEY_CONFLICT});
}
return handleError(error, conn, newRow);
} finally {
Expand Down
4 changes: 3 additions & 1 deletion frontend/components/client/datagridcolumns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const msvGridColumns: GridColDef[] = [
{
field: 'measuredDBH', headerName: 'DBH', headerClassName: 'header', flex: 0.8, align: 'left', editable: true,
valueFormatter: (params: any) => {
if (!params || !params.value) return 0;
const value = Number(params.value);
return value.toFixed(2); // limit trailing decimals to 2 places
}
Expand All @@ -109,6 +110,7 @@ export const msvGridColumns: GridColDef[] = [
{
field: 'measuredHOM', headerName: 'HOM', headerClassName: 'header', flex: 0.5, align: 'left', editable: true,
valueFormatter: (params: any) => {
if (!params || !params.value) return 0;
const value = Number(params.value);
return value.toFixed(2); // limit trailing decimals to 2 places
}
Expand Down Expand Up @@ -152,7 +154,7 @@ export const CoreMeasurementsGridColumns: GridColDef[] = [
{
field: 'measurementDate', headerName: 'MeasurementDate', type: "date", headerClassName: 'header', flex: 1,
valueGetter: (params: any) => {
if (!params.value) return null;
if (!params || !params.value) return null;
return new Date(params.value);
}, editable: true
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"use client";

import { useOrgCensusContext } from "@/app/contexts/userselectionprovider";
import { initialViewFullTableViewRDS } from "@/config/sqlrdsdefinitions/views/viewfulltablerds";
import { initialViewFullTableViewRDS } from "@/config/sqlrdsdefinitions/views/viewfulltableviewrds";
import { Box, Typography } from "@mui/joy";
import { AlertProps } from "@mui/material";
import { GridRowsProp } from "@mui/x-data-grid";
Expand Down Expand Up @@ -42,7 +42,7 @@ export default function ViewFullTableViewDataGrid() {

return (
<>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3, width: '100%' }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3, width: '100%', flexDirection: 'column' }}>
<Box sx={{
width: '100%',
display: 'flex',
Expand Down
7 changes: 1 addition & 6 deletions frontend/components/datagrids/msvdatagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,6 @@ export default function MeasurementSummaryGrid(props: Readonly<MeasurementSummar
}
}, [currentCensus]);

useEffect(() => {
fetchValidationErrors()
.catch(console.error)
.then(() => setRefresh(false));
}, [refresh]);

useEffect(() => {
if (errorRowCount > 0) {
setSnackbar({
Expand Down Expand Up @@ -463,6 +457,7 @@ export default function MeasurementSummaryGrid(props: Readonly<MeasurementSummar

const handleRefresh = async () => {
await fetchPaginatedData(paginationModel.page);
await fetchValidationErrors();
};

const handleError = (error: any, message: string) => {
Expand Down
7 changes: 2 additions & 5 deletions frontend/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ export default function Sidebar(props: SidebarProps) {
{site !== undefined && plot !== undefined && census !== undefined ? (
<Tooltip title={isDataIncomplete ? 'Missing Core Data!' : 'Requirements Met'} arrow disableHoverListener={!isDataIncomplete}>
<Box sx={{ display: 'flex', flex: 1 }}>
<ListItemButton selected={pathname === item.href} sx={{ flex: 1 }} disabled={plot === undefined || census === undefined || isLinkDisabled} color={pathname === item.href ? 'primary' : undefined} onClick={() => {
<ListItemButton selected={pathname === item.href} sx={{ flex: 1 }} disabled={isLinkDisabled} color={pathname === item.href ? 'primary' : undefined} onClick={() => {
if (!isLinkDisabled) {
router.push(item.href);
}
Expand Down Expand Up @@ -821,12 +821,9 @@ export default function Sidebar(props: SidebarProps) {
<Tooltip title={tooltipMessage} arrow disableHoverListener={!isDataIncomplete}>
<Box sx={{ display: 'flex', flex: 1 }}>
<ListItemButton sx={{ flex: 1 }} selected={pathname == (item.href + link.href)}
disabled={plot === undefined || census === undefined || isLinkDisabled} onClick={() => {
disabled={isLinkDisabled} onClick={() => {
if (!isLinkDisabled) {
router.push(item.href + link.href);
if (setToggle) {
setToggle(false); // Close the menu
}
}
}}>
<Badge color={link.href === '/summary' ? "warning" : "danger"}
Expand Down
2 changes: 1 addition & 1 deletion frontend/config/datagridhelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {getAllTaxonomiesViewHCs} from './sqlrdsdefinitions/views/alltaxonomiesvi
import {getMeasurementsSummaryViewHCs} from './sqlrdsdefinitions/views/measurementssummaryviewrds';
import {getStemDimensionsViewHCs} from './sqlrdsdefinitions/views/stemdimensionsviewrds';
import {getStemTaxonomiesViewHCs} from './sqlrdsdefinitions/views/stemtaxonomiesviewrds';
import { getAllViewFullTableViewsHCs } from './sqlrdsdefinitions/views/viewfulltablerds';
import { getAllViewFullTableViewsHCs } from './sqlrdsdefinitions/views/viewfulltableviewrds';

export interface FieldTemplate {
type: 'string' | 'number' | 'boolean' | 'array' | 'date' | 'unknown'
Expand Down
5 changes: 4 additions & 1 deletion frontend/config/datamapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import {PlotsMapper} from "./sqlrdsdefinitions/tables/plotrds";
import {StemTaxonomiesMapper} from "./sqlrdsdefinitions/views/stemtaxonomiesviewrds";
import {QuadratPersonnelMapper} from "./sqlrdsdefinitions/tables/quadratpersonnelrds";
import moment from "moment";
import {ViewFullTableMapper} from "@/config/sqlrdsdefinitions/views/viewfulltableviewrds";

export function parseDate(date: any): Date | undefined {
if (!date || date === null) return undefined;
if (!date) return undefined;
// Check if date is a number (UNIX timestamp), string, or already a Date object
if (typeof date === 'number') {
return moment(new Date(date * 1000)).utc().toDate(); // Convert UNIX timestamp to milliseconds
Expand Down Expand Up @@ -73,6 +74,8 @@ class MapperFactory {
return new StemTaxonomiesMapper() as any;
case 'measurementssummaryview':
return new MeasurementsSummaryMapper() as any;
case 'viewfulltableview':
return new ViewFullTableMapper() as any;
default:
throw new Error('Mapper not found for type: ' + type);
}
Expand Down
7 changes: 7 additions & 0 deletions frontend/config/macros/siteconfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import WidgetsIcon from '@mui/icons-material/Widgets';
import SchemaIcon from '@mui/icons-material/Schema';
import FilterIcon from '@mui/icons-material/FilterList';
import HistoryIcon from '@mui/icons-material/History';
import React from "react";
import {UnifiedValidityFlags} from '../macros';

Expand Down Expand Up @@ -76,6 +77,12 @@ export const siteConfigNav: SiteConfigProps[] = [
tip: "uploaded file display",
icon: CloudCircleIcon,
},
{
label: "View All Historical Data",
href: "/viewfulltable",
tip: "all historical data view",
icon: HistoryIcon
}
],
},

Expand Down
Loading

0 comments on commit efc9d5d

Please sign in to comment.