diff --git a/frontend/app/(hub)/layout.tsx b/frontend/app/(hub)/layout.tsx index 56684504..33f168b3 100644 --- a/frontend/app/(hub)/layout.tsx +++ b/frontend/app/(hub)/layout.tsx @@ -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); @@ -322,7 +322,7 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { paddingBottom: '20px', flexDirection: 'column', }}> - {renderSwitch(usePathname())} + {renderSwitch(pathname)} ; +} \ No newline at end of file diff --git a/frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts b/frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts index 2c28f360..7ec87ed7 100644 --- a/frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts +++ b/frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts @@ -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> { if (!params.slugs || params.slugs.length < 5) throw new Error("slugs not received."); @@ -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.* @@ -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; @@ -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 @@ -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) { @@ -180,7 +187,7 @@ export async function GET(request: NextRequest, { params }: { output: rows, deprecated: deprecatedRows, totalCount: totalRows - }), { status: HTTPResponses.OK }); + }), {status: HTTPResponses.OK}); } else { const mapper = MapperFactory.getMapper(params.dataType); const rows = mapper.mapData(paginatedResults); @@ -188,7 +195,7 @@ export async function GET(request: NextRequest, { params }: { output: rows, deprecated: undefined, totalCount: totalRows - }), { status: HTTPResponses.OK }); + }), {status: HTTPResponses.OK}); } } catch (error: any) { if (conn) await conn.rollback(); @@ -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(); @@ -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 { @@ -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(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 { @@ -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 { @@ -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 = { - '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(); @@ -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(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 { diff --git a/frontend/components/client/datagridcolumns.tsx b/frontend/components/client/datagridcolumns.tsx index 9061f03e..21833cde 100644 --- a/frontend/components/client/datagridcolumns.tsx +++ b/frontend/components/client/datagridcolumns.tsx @@ -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 } @@ -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 } @@ -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 }, diff --git a/frontend/components/datagrids/applications/viewfulltableviewdatagrid.tsx b/frontend/components/datagrids/applications/viewfulltableviewdatagrid.tsx index e6654334..f106536e 100644 --- a/frontend/components/datagrids/applications/viewfulltableviewdatagrid.tsx +++ b/frontend/components/datagrids/applications/viewfulltableviewdatagrid.tsx @@ -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"; @@ -42,7 +42,7 @@ export default function ViewFullTableViewDataGrid() { return ( <> - + { - fetchValidationErrors() - .catch(console.error) - .then(() => setRefresh(false)); - }, [refresh]); - useEffect(() => { if (errorRowCount > 0) { setSnackbar({ @@ -463,6 +457,7 @@ export default function MeasurementSummaryGrid(props: Readonly { await fetchPaginatedData(paginationModel.page); + await fetchValidationErrors(); }; const handleError = (error: any, message: string) => { diff --git a/frontend/components/sidebar.tsx b/frontend/components/sidebar.tsx index b1d43f05..a5de1441 100644 --- a/frontend/components/sidebar.tsx +++ b/frontend/components/sidebar.tsx @@ -758,7 +758,7 @@ export default function Sidebar(props: SidebarProps) { {site !== undefined && plot !== undefined && census !== undefined ? ( - { + { if (!isLinkDisabled) { router.push(item.href); } @@ -821,12 +821,9 @@ export default function Sidebar(props: SidebarProps) { { + disabled={isLinkDisabled} onClick={() => { if (!isLinkDisabled) { router.push(item.href + link.href); - if (setToggle) { - setToggle(false); // Close the menu - } } }}> ({ id: index + indexOffset, coreMeasurementID: item.CoreMeasurementID != null ? Number(item.CoreMeasurementID) : undefined, + censusID: item.CensusID !== null ? Number(item.CoreMeasurementID) : undefined, stemID: item.StemID != null ? Number(item.StemID) : undefined, isValidated: item.IsValidated != null ? bitToBoolean(item.IsValidated) : undefined, measurementDate: parseDate(item.MeasurementDate), @@ -49,6 +52,7 @@ export class CoreMeasurementsMapper implements IDataMapper ({ CoreMeasurementID: item.coreMeasurementID != undefined ? Number(item.coreMeasurementID) : null, + CensusID: item.censusID !== undefined ? Number(item.coreMeasurementID) : null, StemID: item.stemID != undefined ? Number(item.stemID) : null, IsValidated: item.isValidated != undefined ? booleanToBit(item.isValidated) : null, MeasurementDate: parseDate(item.measurementDate), diff --git a/frontend/config/sqlrdsdefinitions/views/viewfulltablerds.ts b/frontend/config/sqlrdsdefinitions/views/viewfulltableviewrds.ts similarity index 77% rename from frontend/config/sqlrdsdefinitions/views/viewfulltablerds.ts rename to frontend/config/sqlrdsdefinitions/views/viewfulltableviewrds.ts index 4712b3f8..4a374d2f 100644 --- a/frontend/config/sqlrdsdefinitions/views/viewfulltablerds.ts +++ b/frontend/config/sqlrdsdefinitions/views/viewfulltableviewrds.ts @@ -1,15 +1,24 @@ // viewfulltableview custom data type -import { ColumnStates, Common, Unique } from "@/config/macros"; -import { MeasurementsSummaryMapper, MeasurementsSummaryRDS, MeasurementsSummaryResult } from "./measurementssummaryviewrds"; -import { StemTaxonomiesMapper, StemTaxonomiesViewRDS, StemTaxonomiesViewResult } from "./stemtaxonomiesviewrds"; -import { IDataMapper } from "@/config/datamapper"; +import {ColumnStates, Common, Unique} from "@/config/macros"; +import { + MeasurementsSummaryMapper, + MeasurementsSummaryRDS, + MeasurementsSummaryResult +} from "./measurementssummaryviewrds"; +import {StemTaxonomiesMapper, StemTaxonomiesViewRDS, StemTaxonomiesViewResult} from "./stemtaxonomiesviewrds"; +import {IDataMapper} from "@/config/datamapper"; -export type ViewFullTableViewRDS = Common & Unique; -export type ViewFullTableViewResult = Common & Unique; +export type ViewFullTableViewRDS = + Common + & Unique; +export type ViewFullTableViewResult = + Common + & Unique; export class ViewFullTableMapper implements IDataMapper { private measurementsSummaryMapper = new MeasurementsSummaryMapper(); private stemTaxonomiesMapper = new StemTaxonomiesMapper(); + demapData(results: ViewFullTableViewRDS[]): ViewFullTableViewResult[] { const measurementsResults = this.measurementsSummaryMapper.demapData(results as MeasurementsSummaryRDS[]); const stemTaxonomiesResults = this.stemTaxonomiesMapper.demapData(results as StemTaxonomiesViewRDS[]); @@ -90,5 +99,6 @@ export function getAllViewFullTableViewsHCs(): ColumnStates { personnelID: false, familyID: false, genusID: false, + subquadratName: false, }; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/sqlscripting/migration_no_mapping.sql b/frontend/sqlscripting/migration_no_mapping.sql index 7d98e175..0ac40888 100644 --- a/frontend/sqlscripting/migration_no_mapping.sql +++ b/frontend/sqlscripting/migration_no_mapping.sql @@ -1,9 +1,49 @@ SET foreign_key_checks = 0; --- stable_sinharaja: old ctfsweb schema +-- stable_mpala: old ctfsweb schema -- forestgeo_scbi: new schema. -- make sure you replace this for each new schema you pull/push from/to. +truncate attributes; +truncate census; +truncate cmattributes; +truncate cmverrors; +truncate coremeasurements; +truncate family; +truncate genus; +truncate personnel; +truncate plots; +truncate quadratpersonnel; +truncate quadrats; +truncate reference; +truncate roles; +truncate species; +truncate specieslimits; +truncate specimens; +truncate stems; +truncate subquadrats; +truncate unifiedchangelog; +truncate validationchangelog; + +DROP VIEW IF EXISTS `alltaxonomiesview`; +DROP VIEW IF EXISTS `measurementssummaryview`; +DROP VIEW IF EXISTS `stemtaxonomiesview`; +DROP VIEW IF EXISTS `viewfulltableview`; + +DROP PROCEDURE IF EXISTS `UpdateValidationStatus`; +DROP PROCEDURE IF EXISTS `ValidateDBHGrowthExceedsMax`; +DROP PROCEDURE IF EXISTS `ValidateDBHShrinkageExceedsMax`; +DROP PROCEDURE IF EXISTS `ValidateFindAllInvalidSpeciesCodes`; +DROP PROCEDURE IF EXISTS `ValidateFindDuplicatedQuadratsByName`; +DROP PROCEDURE IF EXISTS `ValidateFindDuplicateStemTreeTagCombinationsPerCensus`; +DROP PROCEDURE IF EXISTS `ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat`; +DROP PROCEDURE IF EXISTS `ValidateFindStemsInTreeWithDifferentSpecies`; +DROP PROCEDURE IF EXISTS `ValidateFindStemsOutsidePlots`; +DROP PROCEDURE IF EXISTS `ValidateFindTreeStemsInDifferentQuadrats`; +DROP PROCEDURE IF EXISTS `ValidateHOMUpperAndLowerBounds`; +DROP PROCEDURE IF EXISTS `ValidateScreenMeasuredDiameterMinMax`; +DROP PROCEDURE IF EXISTS `ValidateScreenStemsWithMeasurementsButDeadAttributes`; + # attributes DROP TRIGGER IF EXISTS after_insert_attributes; DROP TRIGGER IF EXISTS after_update_attributes; @@ -122,9 +162,9 @@ SELECT s.PlotID, IF(s.GUOM IN ('km', 'hm', 'dam', 'm', 'dm', 'cm', 'mm'), s.GUOM, 'm'), s.ShapeOfSite, LEFT(s.DescriptionOfSite, 65535) -FROM stable_sinharaja.Site s - LEFT JOIN stable_sinharaja.Country c ON s.CountryID = c.CountryID - LEFT JOIN stable_sinharaja.Coordinates co ON s.PlotID = co.PlotID +FROM stable_mpala.Site s + LEFT JOIN stable_mpala.Country c ON s.CountryID = c.CountryID + LEFT JOIN stable_mpala.Coordinates co ON s.PlotID = co.PlotID GROUP BY s.PlotID, s.PlotName, s.LocationName, c.CountryName, s.QDimX, s.QDimY, s.PUOM, s.Area, s.GUOM, co.GX, co.GY, co.GZ, s.ShapeOfSite, s.DescriptionOfSite ON DUPLICATE KEY UPDATE PlotName = IF(VALUES(PlotName) != '', VALUES(PlotName), plots.PlotName), @@ -150,7 +190,7 @@ SELECT r.ReferenceID, r.FullReference, IF(CAST(r.DateofPublication AS CHAR) = '0000-00-00', NULL, r.DateofPublication) AS DateOfPublication, NULL -FROM stable_sinharaja.reference r +FROM stable_mpala.reference r ON DUPLICATE KEY UPDATE PublicationTitle = IF(VALUES(PublicationTitle) != '', VALUES(PublicationTitle), reference.PublicationTitle), FullReference = IF(VALUES(FullReference) != '', VALUES(FullReference), @@ -160,14 +200,14 @@ ON DUPLICATE KEY UPDATE PublicationTitle = IF(VALUES(PublicationTitle -- Insert into family with ON DUPLICATE KEY UPDATE INSERT INTO family (FamilyID, Family, ReferenceID) SELECT f.FamilyID, f.Family, f.ReferenceID -FROM stable_sinharaja.family f +FROM stable_mpala.family f ON DUPLICATE KEY UPDATE Family = IF(VALUES(Family) != '', VALUES(Family), family.Family), ReferenceID = VALUES(ReferenceID); -- Insert into genus with ON DUPLICATE KEY UPDATE INSERT INTO genus (GenusID, FamilyID, Genus, ReferenceID, GenusAuthority) SELECT g.GenusID, g.FamilyID, g.Genus, g.ReferenceID, g.Authority -FROM stable_sinharaja.genus g +FROM stable_mpala.genus g ON DUPLICATE KEY UPDATE FamilyID = VALUES(FamilyID), Genus = IF(VALUES(Genus) != '', VALUES(Genus), genus.Genus), ReferenceID = VALUES(ReferenceID), @@ -188,9 +228,9 @@ SELECT sp.SpeciesID, LEFT(sp.Description, 65535), NULL, sp.ReferenceID -FROM stable_sinharaja.species sp - LEFT JOIN stable_sinharaja.subspecies subs ON sp.SpeciesID = subs.SpeciesID - LEFT JOIN stable_sinharaja.reference ref ON sp.ReferenceID = ref.ReferenceID +FROM stable_mpala.species sp + LEFT JOIN stable_mpala.subspecies subs ON sp.SpeciesID = subs.SpeciesID + LEFT JOIN stable_mpala.reference ref ON sp.ReferenceID = ref.ReferenceID GROUP BY sp.SpeciesID, sp.GenusID, sp.Mnemonic, sp.IDLevel, sp.Authority, sp.FieldFamily, sp.Description, sp.ReferenceID ON DUPLICATE KEY UPDATE GenusID = VALUES(GenusID), SpeciesCode = VALUES(SpeciesCode), @@ -207,40 +247,68 @@ ON DUPLICATE KEY UPDATE GenusID = VALUES(GenusID), ValidCode = VALUES(ValidCode), ReferenceID = VALUES(ReferenceID); -UPDATE stable_sinharaja.census +UPDATE stable_mpala.census SET StartDate = NULL WHERE CAST(StartDate AS CHAR(10)) = '0000-00-00'; -- Insert into census with ON DUPLICATE KEY UPDATE INSERT INTO census (CensusID, PlotID, StartDate, EndDate, Description, PlotCensusNumber) -SELECT c.CensusID, c.PlotID, c.StartDate, c.EndDate, LEFT(c.Description, 65535), c.PlotCensusNumber -FROM stable_sinharaja.census c -ON DUPLICATE KEY UPDATE PlotID = VALUES(PlotID), - StartDate = VALUES(StartDate), - EndDate = VALUES(EndDate), - Description = IF(VALUES(Description) != '', VALUES(Description), census.Description), - PlotCensusNumber = VALUES(PlotCensusNumber); +SELECT + c.CensusID, + c.PlotID, + COALESCE(MIN(d.ExactDate), c.StartDate) AS StartDate, + COALESCE(MAX(d.ExactDate), c.EndDate) AS EndDate, + LEFT(c.Description, 65535), + c.PlotCensusNumber +FROM + stable_mpala.census c +LEFT JOIN + stable_mpala.dbh d ON c.CensusID = d.CensusID +GROUP BY + c.CensusID +ON DUPLICATE KEY UPDATE + PlotID = VALUES(PlotID), + StartDate = VALUES(StartDate), + EndDate = VALUES(EndDate), + Description = IF(VALUES(Description) != '', VALUES(Description), census.Description), + PlotCensusNumber = VALUES(PlotCensusNumber); -- Insert into roles table INSERT INTO roles (RoleID, RoleName, RoleDescription) SELECT RoleID, Description, NULL -FROM stable_sinharaja.rolereference +FROM stable_mpala.rolereference ON DUPLICATE KEY UPDATE RoleName = VALUES(RoleName), RoleDescription = VALUES(RoleDescription); -- Insert into personnel, ensuring each personnel is re-added for each CensusID with new PersonnelID -INSERT INTO personnel (CensusID, FirstName, LastName, RoleID) +-- Step 1: Create a temporary table to hold the intermediate results +CREATE TEMPORARY TABLE tmp_personnel SELECT c.CensusID, p.FirstName, p.LastName, pr.RoleID FROM - stable_sinharaja.personnel p + stable_mpala.personnel p CROSS JOIN - stable_sinharaja.census c + stable_mpala.census c JOIN - stable_sinharaja.personnelrole pr ON p.PersonnelID = pr.PersonnelID; + stable_mpala.personnelrole pr ON p.PersonnelID = pr.PersonnelID; + +-- Step 2: Insert into personnel from the temporary table, handling duplicates +INSERT INTO personnel (CensusID, FirstName, LastName, RoleID) +SELECT + CensusID, + FirstName, + LastName, + RoleID +FROM + tmp_personnel +ON DUPLICATE KEY UPDATE + RoleID = VALUES(RoleID); + +-- Step 3: Drop the temporary table +DROP TEMPORARY TABLE tmp_personnel; -- Insert into quadrats with ON DUPLICATE KEY UPDATE INSERT INTO quadrats (QuadratID, PlotID, CensusID, QuadratName, StartX, StartY, DimensionX, DimensionY, DimensionUnits, @@ -258,10 +326,10 @@ SELECT q.QuadratID, IF(s.QUOM IN ('km2', 'hm2', 'dam2', 'm2', 'dm2', 'cm2', 'mm2'), s.QUOM, 'm2'), IF(q.IsStandardShape = 'Y', 'standard', 'not standard'), IF(s.GUOM IN ('km', 'hm', 'dam', 'm', 'dm', 'cm', 'mm'), s.GUOM, 'm') -FROM stable_sinharaja.quadrat q - LEFT JOIN stable_sinharaja.censusquadrat cq ON q.QuadratID = cq.QuadratID - LEFT JOIN stable_sinharaja.Coordinates co ON q.QuadratID = co.QuadratID - LEFT JOIN stable_sinharaja.Site s ON q.PlotID = s.PlotID +FROM stable_mpala.quadrat q + LEFT JOIN stable_mpala.censusquadrat cq ON q.QuadratID = cq.QuadratID + LEFT JOIN stable_mpala.Coordinates co ON q.QuadratID = co.QuadratID + LEFT JOIN stable_mpala.Site s ON q.PlotID = s.PlotID GROUP BY q.QuadratID, q.PlotID, cq.CensusID, q.QuadratName, s.QDimX, s.QDimY, s.QUOM, q.Area, q.IsStandardShape, s.GUOM ON DUPLICATE KEY UPDATE PlotID = VALUES(PlotID), CensusID = VALUES(CensusID), @@ -279,7 +347,7 @@ ON DUPLICATE KEY UPDATE PlotID = VALUES(PlotID), -- Insert into trees with ON DUPLICATE KEY UPDATE INSERT INTO trees (TreeID, TreeTag, SpeciesID) SELECT t.TreeID, t.Tag, t.SpeciesID -FROM stable_sinharaja.tree t +FROM stable_mpala.tree t ON DUPLICATE KEY UPDATE TreeTag = IF(VALUES(TreeTag) != '', VALUES(TreeTag), trees.TreeTag), SpeciesID = VALUES(SpeciesID); @@ -296,9 +364,9 @@ SELECT s.StemID, IF(si.QUOM IN ('km', 'hm', 'dam', 'm', 'dm', 'cm', 'mm'), si.QUOM, 'm') AS CoordinateUnits, IF(s.Moved = 'Y', 1, 0) AS Moved, LEFT(s.StemDescription, 65535) -FROM stable_sinharaja.stem s - LEFT JOIN stable_sinharaja.quadrat q ON q.QuadratID = s.QuadratID - LEFT JOIN stable_sinharaja.Site si ON q.PlotID = si.PlotID +FROM stable_mpala.stem s + LEFT JOIN stable_mpala.quadrat q ON q.QuadratID = s.QuadratID + LEFT JOIN stable_mpala.Site si ON q.PlotID = si.PlotID GROUP BY s.StemID, s.TreeID, s.QuadratID, s.StemNumber, s.StemTag, s.Moved, s.StemDescription, si.QUOM ON DUPLICATE KEY UPDATE TreeID = VALUES(TreeID), QuadratID = VALUES(QuadratID), @@ -312,19 +380,20 @@ ON DUPLICATE KEY UPDATE TreeID = VALUES(TreeID), stems.StemDescription); -- Insert into coremeasurements with ON DUPLICATE KEY UPDATE -INSERT INTO coremeasurements (CoreMeasurementID, StemID, IsValidated, MeasurementDate, MeasuredDBH, DBHUnit, +INSERT INTO coremeasurements (CoreMeasurementID, CensusID, StemID, IsValidated, MeasurementDate, MeasuredDBH, DBHUnit, MeasuredHOM, HOMUnit, Description, UserDefinedFields) SELECT dbh.DBHID, + dbh.CensusID, dbh.StemID, NULL, dbh.ExactDate, - dbh.DBH, + CAST(dbh.DBH AS DECIMAL(10, 6)), 'cm', - dbh.HOM, + CAST(dbh.HOM AS DECIMAL(10, 6)), 'm', LEFT(dbh.Comments, 65535), NULL -FROM stable_sinharaja.dbh dbh +FROM stable_mpala.dbh dbh ON DUPLICATE KEY UPDATE StemID = VALUES(StemID), IsValidated = VALUES(IsValidated), MeasurementDate = VALUES(MeasurementDate), @@ -339,8 +408,8 @@ ON DUPLICATE KEY UPDATE StemID = VALUES(StemID), -- Insert into quadratpersonnel with ON DUPLICATE KEY UPDATE INSERT INTO quadratpersonnel (QuadratPersonnelID, QuadratID, PersonnelID, CensusID) SELECT dc.DataCollectionID, dc.QuadratID, pr.PersonnelID, dc.CensusID -FROM stable_sinharaja.datacollection dc - JOIN stable_sinharaja.personnelrole pr ON dc.PersonnelRoleID = pr.PersonnelRoleID +FROM stable_mpala.datacollection dc + JOIN stable_mpala.personnelrole pr ON dc.PersonnelRoleID = pr.PersonnelRoleID ON DUPLICATE KEY UPDATE QuadratID = VALUES(QuadratID), PersonnelID = VALUES(PersonnelID), CensusID = VALUES(CensusID); @@ -351,7 +420,7 @@ SELECT ta.TSMCode, LEFT(ta.Description, 65535), IF(ta.Status IN ('alive', 'alive-not measured', 'dead', 'stem dead', 'broken below', 'omitted', 'missing'), ta.Status, NULL) -FROM stable_sinharaja.tsmattributes ta +FROM stable_mpala.tsmattributes ta GROUP BY ta.TSMCode, ta.Description, ta.Status ON DUPLICATE KEY UPDATE Description = IF(VALUES(Description) != '', VALUES(Description), attributes.Description), Status = VALUES(Status); @@ -359,8 +428,8 @@ ON DUPLICATE KEY UPDATE Description = IF(VALUES(Description) != '', VALUES(Descr -- Insert into cmattributes with ON DUPLICATE KEY UPDATE INSERT INTO cmattributes (CMAID, CoreMeasurementID, Code) SELECT dbha.DBHAttID, dbha.DBHID, ta.TSMCode -FROM stable_sinharaja.dbhattributes dbha - JOIN stable_sinharaja.tsmattributes ta ON dbha.TSMID = ta.TSMID +FROM stable_mpala.dbhattributes dbha + JOIN stable_mpala.tsmattributes ta ON dbha.TSMID = ta.TSMID ON DUPLICATE KEY UPDATE CoreMeasurementID = VALUES(CoreMeasurementID), Code = VALUES(Code); @@ -377,9 +446,9 @@ SELECT sp.SpecimenID, sp.CollectionDate, sp.DeterminedBy, LEFT(sp.Description, 65535) -FROM stable_sinharaja.specimen sp - LEFT JOIN stable_sinharaja.stem st ON st.TreeID = sp.TreeID - LEFT JOIN stable_sinharaja.personnel pr ON sp.Collector = CONCAT(pr.FirstName, ' ', pr.LastName) +FROM stable_mpala.specimen sp + LEFT JOIN stable_mpala.stem st ON st.TreeID = sp.TreeID + LEFT JOIN stable_mpala.personnel pr ON sp.Collector = CONCAT(pr.FirstName, ' ', pr.LastName) ON DUPLICATE KEY UPDATE StemID = VALUES(StemID), PersonnelID = VALUES(PersonnelID), SpecimenNumber = VALUES(SpecimenNumber), @@ -1532,7 +1601,6 @@ BEGIN SET new_json = JSON_OBJECT( 'SpeciesID', NEW.SpeciesID, 'GenusID', NEW.GenusID, - 'CensusID', NEW.CensusID, 'SpeciesCode', NEW.SpeciesCode, 'SpeciesName', NEW.SpeciesName, 'SubspeciesName', NEW.SubspeciesName, @@ -1573,7 +1641,6 @@ BEGIN SET old_json = JSON_OBJECT( 'SpeciesID', OLD.SpeciesID, 'GenusID', OLD.GenusID, - 'CensusID', OLD.CensusID, 'SpeciesCode', OLD.SpeciesCode, 'SpeciesName', OLD.SpeciesName, 'SubspeciesName', OLD.SubspeciesName, @@ -1590,7 +1657,6 @@ BEGIN SET new_json = JSON_OBJECT( 'SpeciesID', NEW.SpeciesID, 'GenusID', NEW.GenusID, - 'CensusID', NEW.CensusID, 'SpeciesCode', NEW.SpeciesCode, 'SpeciesName', NEW.SpeciesName, 'SubspeciesName', NEW.SubspeciesName, @@ -1631,7 +1697,6 @@ BEGIN SET old_json = JSON_OBJECT( 'SpeciesID', OLD.SpeciesID, 'GenusID', OLD.GenusID, - 'CensusID', OLD.CensusID, 'SpeciesCode', OLD.SpeciesCode, 'SpeciesName', OLD.SpeciesName, 'SubspeciesName', OLD.SubspeciesName, @@ -2712,3 +2777,1712 @@ BEGIN END // DELIMITER ; + +CREATE VIEW alltaxonomiesview AS +SELECT + s.SpeciesID AS SpeciesID, + f.FamilyID AS FamilyID, + g.GenusID AS GenusID, + r.ReferenceID AS ReferenceID, + s.SpeciesCode AS SpeciesCode, + f.Family AS Family, + g.Genus AS Genus, + g.GenusAuthority AS GenusAuthority, + s.SpeciesName AS SpeciesName, + s.SubspeciesName AS SubSpeciesName, + s.IDLevel AS SpeciesIDLevel, + s.SpeciesAuthority AS SpeciesAuthority, + s.SubspeciesAuthority AS SubspeciesAuthority, + s.ValidCode AS ValidCode, + s.FieldFamily AS FieldFamily, + s.Description AS SpeciesDescription, + r.PublicationTitle AS PublicationTitle, + r.FullReference AS FullReference, + r.DateOfPublication AS DateOfPublication, + r.Citation AS Citation +FROM + family f + JOIN genus g ON f.FamilyID = g.FamilyID + JOIN species s ON g.GenusID = s.GenusID + LEFT JOIN reference r ON s.ReferenceID = r.ReferenceID; + + +CREATE VIEW measurementssummaryview AS +SELECT + cm.CoreMeasurementID AS CoreMeasurementID, + p.PlotID AS PlotID, + cm.CensusID AS CensusID, + q.QuadratID AS QuadratID, + s.SpeciesID AS SpeciesID, + t.TreeID AS TreeID, + st.StemID AS StemID, + qp.PersonnelID AS PersonnelID, + p.PlotName AS PlotName, + q.QuadratName AS QuadratName, + s.SpeciesCode AS SpeciesCode, + t.TreeTag AS TreeTag, + st.StemTag AS StemTag, + st.LocalX AS StemLocalX, + st.LocalY AS StemLocalY, + st.CoordinateUnits AS StemUnits, + COALESCE(CONCAT(pe.FirstName, ' ', pe.LastName), 'Unknown') AS PersonnelName, + cm.MeasurementDate AS MeasurementDate, + cm.MeasuredDBH AS MeasuredDBH, + cm.DBHUnit AS DBHUnits, + cm.MeasuredHOM AS MeasuredHOM, + cm.HOMUnit AS HOMUnits, + cm.IsValidated AS IsValidated, + cm.Description AS Description, + (SELECT GROUP_CONCAT(ca.Code SEPARATOR '; ') + FROM cmattributes ca + WHERE ca.CoreMeasurementID = cm.CoreMeasurementID) AS Attributes +FROM + coremeasurements cm + LEFT JOIN stems st ON cm.StemID = st.StemID + LEFT JOIN trees t ON st.TreeID = t.TreeID + LEFT JOIN species s ON t.SpeciesID = s.SpeciesID + LEFT JOIN genus g ON s.GenusID = g.GenusID + LEFT JOIN family f ON g.FamilyID = f.FamilyID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + LEFT JOIN plots p ON q.PlotID = p.PlotID + LEFT JOIN census c ON cm.CensusID = c.CensusID + LEFT JOIN quadratpersonnel qp ON q.QuadratID = qp.QuadratID + LEFT JOIN personnel pe ON qp.PersonnelID = pe.PersonnelID +GROUP BY + cm.CoreMeasurementID, p.PlotID, cm.CensusID, q.QuadratID, s.SpeciesID, t.TreeID, st.StemID, qp.PersonnelID, + p.PlotName, q.QuadratName, s.SpeciesCode, t.TreeTag, st.StemTag, st.LocalX, st.LocalY, st.CoordinateUnits, + pe.FirstName, pe.LastName, cm.MeasurementDate, cm.MeasuredDBH, cm.DBHUnit, cm.MeasuredHOM, cm.HOMUnit, + cm.IsValidated, cm.Description; + + +CREATE VIEW stemtaxonomiesview AS +SELECT + s.StemID AS StemID, + t.TreeID AS TreeID, + q.QuadratID AS QuadratID, + c.CensusID AS CensusID, + p.PlotID AS PlotID, + f.FamilyID AS FamilyID, + g.GenusID AS GenusID, + sp.SpeciesID AS SpeciesID, + s.StemTag AS StemTag, + t.TreeTag AS TreeTag, + sp.SpeciesCode AS SpeciesCode, + f.Family AS Family, + g.Genus AS Genus, + sp.SpeciesName AS SpeciesName, + sp.SubspeciesName AS SubspeciesName, + sp.ValidCode AS ValidCode, + g.GenusAuthority AS GenusAuthority, + sp.SpeciesAuthority AS SpeciesAuthority, + sp.SubspeciesAuthority AS SubspeciesAuthority, + sp.IDLevel AS SpeciesIDLevel, + sp.FieldFamily AS SpeciesFieldFamily +FROM + stems s + JOIN trees t ON s.TreeID = t.TreeID + JOIN quadrats q ON s.QuadratID = q.QuadratID + JOIN census c ON q.CensusID = c.CensusID + JOIN plots p ON c.PlotID = p.PlotID + JOIN species sp ON t.SpeciesID = sp.SpeciesID + JOIN genus g ON sp.GenusID = g.GenusID + LEFT JOIN family f ON g.FamilyID = f.FamilyID; + + +CREATE VIEW viewfulltableview AS +SELECT + cm.CoreMeasurementID AS CoreMeasurementID, + cm.MeasurementDate AS MeasurementDate, + cm.MeasuredDBH AS MeasuredDBH, + cm.DBHUnit AS DBHUnits, + cm.MeasuredHOM AS MeasuredHOM, + cm.HOMUnit AS HOMUnits, + cm.Description AS Description, + cm.IsValidated AS IsValidated, + p.PlotID AS PlotID, + p.PlotName AS PlotName, + p.LocationName AS LocationName, + p.CountryName AS CountryName, + p.DimensionX AS DimensionX, + p.DimensionY AS DimensionY, + p.Area AS PlotArea, + p.GlobalX AS GlobalX, + p.GlobalY AS GlobalY, + p.GlobalZ AS GlobalZ, + p.DimensionUnits AS PlotUnit, + p.PlotShape AS PlotShape, + p.PlotDescription AS PlotDescription, + c.CensusID AS CensusID, + c.StartDate AS CensusStartDate, + c.EndDate AS CensusEndDate, + c.Description AS CensusDescription, + c.PlotCensusNumber AS PlotCensusNumber, + q.QuadratID AS QuadratID, + q.QuadratName AS QuadratName, + q.DimensionX AS QuadratDimensionX, + q.DimensionY AS QuadratDimensionY, + q.Area AS QuadratArea, + q.QuadratShape AS QuadratShape, + q.DimensionUnits AS QuadratUnit, + sq.SubquadratID AS SubquadratID, + sq.SubquadratName AS SubquadratName, + sq.DimensionX AS SubquadratDimensionX, + sq.DimensionY AS SubquadratDimensionY, + sq.QX AS QX, + sq.QY AS QY, + sq.CoordinateUnits AS SubquadratUnit, + t.TreeID AS TreeID, + t.TreeTag AS TreeTag, + s.StemID AS StemID, + s.StemTag AS StemTag, + s.LocalX AS StemLocalX, + s.LocalY AS StemLocalY, + s.CoordinateUnits AS StemUnits, + per.PersonnelID AS PersonnelID, + per.FirstName AS FirstName, + per.LastName AS LastName, + r.RoleName AS PersonnelRoles, + sp.SpeciesID AS SpeciesID, + sp.SpeciesCode AS SpeciesCode, + sp.SpeciesName AS SpeciesName, + sp.SubspeciesName AS SubspeciesName, + sp.SubspeciesAuthority AS SubspeciesAuthority, + sp.IDLevel AS SpeciesIDLevel, + g.GenusID AS GenusID, + g.Genus AS Genus, + g.GenusAuthority AS GenusAuthority, + fam.FamilyID AS FamilyID, + fam.Family AS Family, + attr.Code AS AttributeCode, + attr.Description AS AttributeDescription, + attr.Status AS AttributeStatus +FROM + coremeasurements cm + LEFT JOIN stems s ON cm.StemID = s.StemID + LEFT JOIN trees t ON s.TreeID = t.TreeID + LEFT JOIN species sp ON t.SpeciesID = sp.SpeciesID + LEFT JOIN genus g ON sp.GenusID = g.GenusID + LEFT JOIN family fam ON g.FamilyID = fam.FamilyID + LEFT JOIN specieslimits sl ON sp.SpeciesCode = sl.SpeciesCode + LEFT JOIN quadrats q ON s.QuadratID = q.QuadratID + LEFT JOIN quadratpersonnel qp ON q.QuadratID = qp.QuadratID + LEFT JOIN personnel per ON qp.PersonnelID = per.PersonnelID + LEFT JOIN plots p ON q.PlotID = p.PlotID + LEFT JOIN subquadrats sq ON q.QuadratID = sq.QuadratID + LEFT JOIN census c ON cm.CensusID = c.CensusID + LEFT JOIN roles r ON per.RoleID = r.RoleID + LEFT JOIN attributes attr ON cm.CoreMeasurementID = attr.Code + LEFT JOIN cmattributes cma ON cm.CoreMeasurementID = cma.CoreMeasurementID AND attr.Code = cma.Code; + +create + definer = azureroot@`%` procedure UpdateValidationStatus(IN p_PlotID int, IN p_CensusID int, OUT RowsValidated int) +BEGIN + -- Create a temporary table to store CoreMeasurementIDs + CREATE TEMPORARY TABLE IF NOT EXISTS TempUpdatedIDs (CoreMeasurementID INT); + + -- Clear the temporary table + TRUNCATE TABLE TempUpdatedIDs; + + -- Insert the CoreMeasurementIDs of the rows to be updated into the temporary table + INSERT INTO TempUpdatedIDs (CoreMeasurementID) + SELECT cm.CoreMeasurementID + FROM coremeasurements cm + LEFT JOIN cmverrors cme ON cm.CoreMeasurementID = cme.CoreMeasurementID + LEFT JOIN stems s on cm.StemID = s.StemID + LEFT JOIN quadrats q on s.QuadratID = q.QuadratID + WHERE cm.IsValidated = FALSE + AND (q.PlotID = p_PlotID OR p_PlotID IS NULL) + AND (q.CensusID = p_CensusID OR p_CensusID IS NULL) + AND cme.CoreMeasurementID IS NULL; + + -- Update the IsValidated column + UPDATE coremeasurements cm + INNER JOIN TempUpdatedIDs tmp ON cm.CoreMeasurementID = tmp.CoreMeasurementID + SET cm.IsValidated = TRUE; + + -- Get the count of rows that have been updated + SET RowsValidated = ROW_COUNT(); + + -- Select the CoreMeasurementIDs from the temporary table + SELECT CoreMeasurementID FROM TempUpdatedIDs; + + -- Optionally, drop the temporary table + DROP TEMPORARY TABLE IF EXISTS TempUpdatedIDs; +END; + +create + definer = azureroot@`%` procedure ValidateDBHGrowthExceedsMax(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE vPrevDBH DECIMAL(10, 2); + DECLARE vCurrDBH DECIMAL(10, 2); + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE done INT DEFAULT FALSE; + DECLARE veID INT; + DECLARE cur CURSOR FOR + SELECT cm2.CoreMeasurementID, cm1.MeasuredDBH, cm2.MeasuredDBH + FROM coremeasurements cm1 + JOIN coremeasurements cm2 + ON cm1.StemID = cm2.StemID + AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 + LEFT JOIN stems st2 ON cm2.StemID = st2.StemID + LEFT JOIN quadrats q ON st2.QuadratID = q.QuadratID + LEFT JOIN cmattributes cma + ON cm1.CoreMeasurementID = cma.CoreMeasurementID + LEFT JOIN attributes a + ON cma.Code = a.Code + WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) + AND cm1.MeasuredDBH IS NOT NULL + AND cm2.MeasuredDBH IS NOT NULL + AND (cm2.MeasuredDBH - cm1.MeasuredDBH > 65) + AND cm1.IsValidated IS TRUE + AND cm2.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm1 + JOIN coremeasurements cm2 + ON cm1.StemID = cm2.StemID + AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 + LEFT JOIN stems st2 ON cm2.StemID = st2.StemID + LEFT JOIN quadrats q ON st2.QuadratID = q.QuadratID + LEFT JOIN cmattributes cma + ON cm1.CoreMeasurementID = cma.CoreMeasurementID + LEFT JOIN attributes a + ON cma.Code = a.Code + WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) + AND cm1.MeasuredDBH IS NOT NULL + AND cm2.MeasuredDBH IS NOT NULL + AND (cm2.MeasuredDBH - cm1.MeasuredDBH > 65) + AND cm1.IsValidated IS TRUE + AND cm2.IsValidated IS FALSE + AND (p_CensusID = -1 OR q.CensusID = p_CensusID) + AND (p_PlotID = -1 OR q.PlotID = p_PlotID); + + -- Fetch the ValidationErrorID for this stored procedure + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateDBHGrowthExceedsMax'; + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID, vPrevDBH, vCurrDBH; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Annual DBH Growth'; + SET measuredValue = CONCAT('Previous DBH: ', vPrevDBH, ', Current DBH: ', vCurrDBH); + SET expectedValueRange = 'Growth <= 65'; + SET additionalDetails = 'Checked for excessive DBH growth over a year'; + + IF vCurrDBH - vPrevDBH > 65 THEN + SET validationResult = 0; + SET errorMessage = 'Growth exceeds max threshold.'; + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateDBHGrowthExceedsMax', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, + insertCount AS FailedRows, + successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateDBHShrinkageExceedsMax(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE vPrevDBH DECIMAL(10, 2); + DECLARE vCurrDBH DECIMAL(10, 2); + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + DECLARE cur CURSOR FOR + SELECT cm2.CoreMeasurementID, cm1.MeasuredDBH, cm2.MeasuredDBH + FROM coremeasurements cm1 + JOIN coremeasurements cm2 + ON cm1.StemID = cm2.StemID + AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 + LEFT JOIN stems st ON cm2.StemID = st.StemID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + LEFT JOIN cmattributes cma + ON cm1.CoreMeasurementID = cma.CoreMeasurementID + LEFT JOIN attributes a + ON cma.Code = a.Code + WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) + AND cm1.MeasuredDBH IS NOT NULL + AND cm2.MeasuredDBH IS NOT NULL + AND cm1.IsValidated IS TRUE + AND cm2.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm1 + JOIN coremeasurements cm2 + ON cm1.StemID = cm2.StemID + AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 + LEFT JOIN stems st ON cm2.StemID = st.StemID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + LEFT JOIN cmattributes cma + ON cm1.CoreMeasurementID = cma.CoreMeasurementID + LEFT JOIN attributes a + ON cma.Code = a.Code + WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) + AND cm1.MeasuredDBH IS NOT NULL + AND cm2.MeasuredDBH IS NOT NULL + AND cm1.IsValidated IS TRUE + AND cm2.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateDBHShrinkageExceedsMax'; + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID, vPrevDBH, vCurrDBH; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Annual DBH Shrinkage'; + SET measuredValue = CONCAT('Previous DBH: ', vPrevDBH, ', Current DBH: ', vCurrDBH); + SET expectedValueRange = 'Shrinkage < 5% of previous DBH'; + SET additionalDetails = 'Checked for excessive DBH shrinkage over a year'; + + IF vCurrDBH < vPrevDBH * 0.95 THEN + SET validationResult = 0; + SET errorMessage = 'Shrinkage exceeds maximum allowed threshold.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateDBHShrinkageExceedsMax', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindAllInvalidSpeciesCodes(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE vSpeciesID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID, sp.SpeciesID + FROM stems s + JOIN trees t ON s.TreeID = t.TreeID + LEFT JOIN species sp ON t.SpeciesID = sp.SpeciesID + JOIN coremeasurements cm ON s.StemID = cm.StemID + LEFT JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE sp.SpeciesID IS NULL + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY cm.CoreMeasurementID; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM stems s + JOIN trees t ON s.TreeID = t.TreeID + LEFT JOIN species sp ON t.SpeciesID = sp.SpeciesID + JOIN coremeasurements cm ON s.StemID = cm.StemID + LEFT JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE sp.SpeciesID IS NULL + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY cm.CoreMeasurementID; + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindAllInvalidSpeciesCodes'; + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID, vSpeciesID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Species Code Validation'; + SET measuredValue = CONCAT('Species ID: ', IFNULL(vSpeciesID, 'NULL')); + SET expectedValueRange = 'Non-null and valid Species ID'; + SET additionalDetails = 'Checking for the existence of valid species codes for each measurement.'; + + IF vSpeciesID IS NULL THEN + SET validationResult = 0; + SET errorMessage = 'Invalid species code detected.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindAllInvalidSpeciesCodes', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindDuplicateStemTreeTagCombinationsPerCensus(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + DECLARE cur CURSOR FOR + SELECT SubQuery.CoreMeasurementID + FROM (SELECT cm.CoreMeasurementID + FROM coremeasurements cm + INNER JOIN stems s ON cm.StemID = s.StemID + INNER JOIN trees t ON s.TreeID = t.TreeID + INNER JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + AND cm.IsValidated = FALSE + GROUP BY q.CensusID, s.StemTag, t.TreeTag, cm.CoreMeasurementID + HAVING COUNT(*) > 1) AS SubQuery; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM (SELECT cm.CoreMeasurementID + FROM coremeasurements cm + INNER JOIN stems s ON cm.StemID = s.StemID + INNER JOIN trees t ON s.TreeID = t.TreeID + INNER JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + AND cm.IsValidated = FALSE + GROUP BY q.CensusID, s.StemTag, t.TreeTag, cm.CoreMeasurementID + HAVING COUNT(*) > 1) AS DuplicationCheck; + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindDuplicateStemTreeTagCombinationsPerCensus'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Duplicate Stem-Tree Tag Combinations per Census'; + SET measuredValue = 'N/A'; + SET expectedValueRange = 'Unique Stem-Tree Tag Combinations'; + SET additionalDetails = 'Checking for duplicate stem and tree tag combinations in each census.'; + + IF EXISTS (SELECT 1 + FROM coremeasurements cm + INNER JOIN stems s ON cm.StemID = s.StemID + INNER JOIN trees t ON s.TreeID = t.TreeID + INNER JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE cm.CoreMeasurementID = vCoreMeasurementID + GROUP BY q.CensusID, s.StemTag, t.TreeTag + HAVING COUNT(cm.CoreMeasurementID) > 1) THEN + SET validationResult = 0; + SET errorMessage = 'Duplicate stem and tree tag combination detected.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindDuplicateStemTreeTagCombinationsPerCensus', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindDuplicatedQuadratsByName(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID + FROM quadrats q + LEFT JOIN stems st ON q.QuadratID = st.QuadratID + JOIN coremeasurements cm ON st.StemID = cm.StemID + WHERE cm.IsValidated IS FALSE + AND (q.PlotID, q.QuadratName) IN (SELECT PlotID, QuadratName + FROM quadrats + GROUP BY PlotID, QuadratName + HAVING COUNT(*) > 1) + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY cm.CoreMeasurementID; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM quadrats q + LEFT JOIN stems st ON q.QuadratID = st.QuadratID + JOIN coremeasurements cm ON st.StemID = cm.StemID + WHERE cm.IsValidated IS FALSE + AND (q.PlotID, q.QuadratName) IN (SELECT PlotID, QuadratName + FROM quadrats + GROUP BY PlotID, QuadratName + HAVING COUNT(*) > 1) + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY cm.CoreMeasurementID; + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindDuplicatedQuadratsByName'; + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Quadrat Name Duplication'; + SET measuredValue = 'N/A'; + SET expectedValueRange = 'Unique Quadrat Names per Plot'; + SET additionalDetails = 'Checking for duplicated quadrat names within the same plot.'; + + IF EXISTS (SELECT 1 + FROM quadrats q + WHERE q.QuadratID = vCoreMeasurementID + AND (q.PlotID, q.QuadratName) IN (SELECT PlotID, QuadratName + FROM quadrats + GROUP BY PlotID, QuadratName + HAVING COUNT(*) > 1)) THEN + SET validationResult = 0; + SET errorMessage = 'Duplicated quadrat name detected.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindDuplicatedQuadratsByName', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + DECLARE cur CURSOR FOR + SELECT MIN(cm.CoreMeasurementID) AS CoreMeasurementID + FROM coremeasurements cm + JOIN stems st ON cm.StemID = st.StemID + JOIN quadrats q ON st.QuadratID = q.QuadratID + JOIN census c ON q.CensusID = c.CensusID + WHERE (cm.MeasurementDate < c.StartDate OR cm.MeasurementDate > c.EndDate) + AND cm.MeasurementDate IS NOT NULL + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR c.PlotID = p_PlotID) + GROUP BY q.QuadratID, c.CensusID, c.StartDate, c.EndDate; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm + JOIN stems st ON cm.StemID = st.StemID + JOIN quadrats q ON st.QuadratID = q.QuadratID + JOIN census c ON q.CensusID = c.CensusID + WHERE (cm.MeasurementDate < c.StartDate OR cm.MeasurementDate > c.EndDate) + AND cm.MeasurementDate IS NOT NULL + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR c.PlotID = p_PlotID); + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Measurement Date vs Census Date Bounds'; + SET measuredValue = 'Measurement Date'; + SET expectedValueRange = 'Within Census Start and End Dates'; + SET additionalDetails = + 'Checking if measurement dates fall within the start and end dates of their respective censuses.'; + + IF EXISTS (SELECT 1 + FROM coremeasurements cm + JOIN stems st ON cm.StemID = st.StemID + JOIN quadrats q ON st.QuadratID = q.QuadratID + JOIN census c ON q.CensusID = c.CensusID + WHERE cm.CoreMeasurementID = vCoreMeasurementID + AND (cm.MeasurementDate < c.StartDate OR cm.MeasurementDate > c.EndDate)) THEN + SET validationResult = 0; + SET errorMessage = 'Measurement outside census date bounds.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindStemsInTreeWithDifferentSpecies(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID + FROM coremeasurements cm + JOIN stems s ON cm.StemID = s.StemID + JOIN trees t ON s.TreeID = t.TreeID + JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE cm.IsValidated = FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY t.TreeID, cm.CoreMeasurementID + HAVING COUNT(DISTINCT t.SpeciesID) > 1; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm + JOIN stems s ON cm.StemID = s.StemID + JOIN trees t ON s.TreeID = t.TreeID + JOIN quadrats q ON s.QuadratID = q.QuadratID + WHERE cm.IsValidated = FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY t.TreeID + HAVING COUNT(DISTINCT t.SpeciesID) > 1; + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindStemsInTreeWithDifferentSpecies'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Each tree should have a consistent species across all its stems.'; + SET measuredValue = 'Species consistency across tree stems'; + SET expectedValueRange = 'One species per tree'; + SET additionalDetails = 'Checking if stems belonging to the same tree have different species IDs.'; + + IF EXISTS (SELECT 1 + FROM stems s + JOIN trees t ON s.TreeID = t.TreeID + WHERE t.TreeID IN (SELECT TreeID + FROM stems + WHERE StemID IN + (SELECT StemID + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID)) + GROUP BY t.TreeID + HAVING COUNT(DISTINCT t.SpeciesID) > 1) THEN + SET validationResult = 0; + SET errorMessage = 'Stems in the same tree have different species.'; + + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindStemsInTreeWithDifferentSpecies', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = CONCAT('Validation completed. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindStemsOutsidePlots(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID + FROM stems s + INNER JOIN coremeasurements cm ON s.StemID = cm.StemID + INNER JOIN quadrats q ON s.QuadratID = q.QuadratID + INNER JOIN plots p ON q.PlotID = p.PlotID + WHERE (s.LocalX > p.DimensionX OR s.LocalX > p.DimensionY) + AND s.LocalX IS NOT NULL + AND s.LocalY IS NOT NULL + AND (p.DimensionX > 0 AND p.DimensionY > 0) + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) + GROUP BY cm.CoreMeasurementID; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM stems s + INNER JOIN quadrats q ON s.QuadratID = q.QuadratID + INNER JOIN plots p ON q.PlotID = p.PlotID + INNER JOIN coremeasurements cm ON s.StemID = cm.StemID + WHERE (s.LocalX > p.DimensionX OR s.LocalX > p.DimensionY) + AND s.LocalX IS NOT NULL + AND s.LocalY IS NOT NULL + AND (p.DimensionX > 0 AND p.DimensionY > 0) + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindStemsOutsidePlots'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Stem Placement within Plot Boundaries'; + SET measuredValue = 'Stem Plot Coordinates'; + SET expectedValueRange = 'Within Plot Dimensions'; + SET additionalDetails = 'Validating whether stems are located within the specified plot dimensions.'; + + IF EXISTS (SELECT 1 + FROM stems s + INNER JOIN quadrats q ON s.QuadratID = q.QuadratID + INNER JOIN plots p ON q.PlotID = p.PlotID + WHERE s.StemID IN + (SELECT StemID + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID) + AND (s.LocalX > p.DimensionX OR s.LocalY > p.DimensionY)) THEN + SET validationResult = 0; + SET errorMessage = 'Stem is outside plot dimensions.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindStemsOutsidePlots', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = CONCAT('Validation completed. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateFindTreeStemsInDifferentQuadrats(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + + DECLARE cur CURSOR FOR + SELECT cm1.CoreMeasurementID + FROM stems s1 + JOIN stems s2 ON s1.TreeID = s2.TreeID AND s1.StemID != s2.StemID + JOIN quadrats q1 ON s1.QuadratID = q1.QuadratID + JOIN quadrats q2 ON s2.QuadratID = q2.QuadratID + JOIN coremeasurements cm1 ON s1.StemID = cm1.StemID + WHERE q1.QuadratID != q2.QuadratID + AND cm1.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q1.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q1.PlotID = p_PlotID) + GROUP BY cm1.CoreMeasurementID; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + IF p_CensusID IS NULL THEN + SET p_CensusID = -1; + END IF; + IF p_PlotID IS NULL THEN + SET p_PlotID = -1; + END IF; + + SELECT COUNT(*) + INTO expectedCount + FROM stems s1 + JOIN stems s2 ON s1.TreeID = s2.TreeID AND s1.StemID != s2.StemID + JOIN quadrats q1 ON s1.QuadratID = q1.QuadratID + JOIN quadrats q2 ON s2.QuadratID = q2.QuadratID + JOIN coremeasurements cm1 ON s1.StemID = cm1.StemID + WHERE q1.QuadratID != q2.QuadratID + AND cm1.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q1.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q1.PlotID = p_PlotID) + GROUP BY cm1.CoreMeasurementID; + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateFindTreeStemsInDifferentQuadrats'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Stem Quadrat Consistency within Trees'; + SET measuredValue = 'Quadrat IDs of Stems'; + SET expectedValueRange = 'Consistent Quadrat IDs for all Stems in a Tree'; + SET additionalDetails = 'Validating that all stems within the same tree are located in the same quadrat.'; + + IF EXISTS (SELECT 1 + FROM stems s1 + JOIN stems s2 ON s1.TreeID = s2.TreeID AND s1.StemID != s2.StemID + JOIN quadrats q1 on q1.QuadratID = s2.QuadratID + JOIN quadrats q2 on q2.QuadratID = s2.QuadratID + WHERE s1.StemID IN + (SELECT StemID + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID) + AND q1.QuadratID != q2.QuadratID) THEN + SET validationResult = 0; + SET errorMessage = 'Stems in the same tree are in different quadrats.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateFindTreeStemsInDifferentQuadrats', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = CONCAT('Validation completed. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateHOMUpperAndLowerBounds(IN p_CensusID int, IN p_PlotID int, + IN minHOM decimal(10, 2), IN maxHOM decimal(10, 2)) +BEGIN + DECLARE defaultMinHOM DECIMAL(10, 2); + DECLARE defaultMaxHOM DECIMAL(10, 2); + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID + FROM coremeasurements cm + LEFT JOIN stems st ON cm.StemID = st.StemID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + WHERE ( + (minHOM IS NOT NULL AND MeasuredHOM < minHOM) OR + (maxHOM IS NOT NULL AND MeasuredHOM > maxHOM) OR + (minHOM IS NULL AND maxHOM IS NULL) + ) + AND IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm + LEFT JOIN stems st ON cm.StemID = st.StemID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + WHERE ( + (minHOM IS NOT NULL AND MeasuredHOM < minHOM) OR + (maxHOM IS NOT NULL AND MeasuredHOM > maxHOM) OR + (minHOM IS NULL AND maxHOM IS NULL) + ) + AND IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateHOMUpperAndLowerBounds'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + + IF done THEN + LEAVE loop1; + END IF; + + IF minHOM IS NULL OR maxHOM IS NULL THEN + SELECT COALESCE(sl.LowerBound, 0) AS defaultMinHOM, + COALESCE(sl.UpperBound, 9999) AS defaultMaxHOM + INTO defaultMinHOM, defaultMaxHOM + FROM specieslimits sl + JOIN species s ON sl.SpeciesCode = s.SpeciesCode + JOIN trees t ON s.SpeciesID = t.SpeciesID + JOIN stems st ON t.TreeID = st.TreeID + JOIN coremeasurements cm ON st.StemID = cm.StemID + WHERE cm.CoreMeasurementID = vCoreMeasurementID + AND sl.LimitType = 'HOM'; + + SET minHOM = COALESCE(minHOM, defaultMinHOM); + SET maxHOM = COALESCE(maxHOM, defaultMaxHOM); + END IF; + + SET validationCriteria = 'HOM Measurement Range Validation'; + SET measuredValue = CONCAT('Measured HOM: ', (SELECT MeasuredHOM + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID)); + SET expectedValueRange = CONCAT('Expected HOM Range: ', minHOM, ' - ', maxHOM); + SET additionalDetails = 'Checks if the measured HOM falls within the specified minimum and maximum range.'; + + IF (SELECT MeasuredHOM + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID + AND ( + (minHOM IS NOT NULL AND MeasuredHOM < minHOM) OR + (maxHOM IS NOT NULL AND MeasuredHOM > maxHOM) OR + (minHOM IS NULL AND maxHOM IS NULL) + )) THEN + SET validationResult = 0; + SET errorMessage = CONCAT('HOM outside bounds: ', minHOM, ' - ', maxHOM); + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateHOMUpperAndLowerBounds', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateScreenMeasuredDiameterMinMax(IN p_CensusID int, IN p_PlotID int, + IN minDBH decimal(10, 2), + IN maxDBH decimal(10, 2)) +BEGIN + DECLARE defaultMinDBH DECIMAL(10, 2); + DECLARE defaultMaxDBH DECIMAL(10, 2); + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE measuredValue VARCHAR(255); + DECLARE expectedValueRange VARCHAR(255); + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE veID INT; + DECLARE done INT DEFAULT FALSE; + + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID + FROM coremeasurements cm + LEFT JOIN stems st ON cm.StemID = st.StemID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + WHERE ( + (MeasuredDBH < 0) OR + (maxDBH IS NOT NULL AND MeasuredDBH > maxDBH) OR + (minDBH IS NULL AND maxDBH IS NULL) + ) + AND IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm + LEFT JOIN stems st ON cm.StemID = st.StemID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + WHERE ( + (MeasuredDBH < 0) OR + (maxDBH IS NOT NULL AND MeasuredDBH > maxDBH) OR + (minDBH IS NULL AND maxDBH IS NULL) + ) + AND IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateScreenMeasuredDiameterMinMax'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + + IF done THEN + LEAVE loop1; + END IF; + + IF minDBH IS NULL OR maxDBH IS NULL THEN + SELECT COALESCE(sl.LowerBound, 0) AS defaultMinDBH, + COALESCE(sl.UpperBound, 9999) AS defaultMaxDBH + INTO defaultMinDBH, defaultMaxDBH + FROM specieslimits sl + JOIN species s ON sl.SpeciesCode = s.SpeciesCode + JOIN trees t ON s.SpeciesID = t.SpeciesID + JOIN stems st ON t.TreeID = st.TreeID + JOIN coremeasurements cm ON st.StemID = cm.StemID + WHERE cm.CoreMeasurementID = vCoreMeasurementID + AND sl.LimitType = 'DBH'; + + SET minDBH = COALESCE(minDBH, defaultMinDBH); + SET maxDBH = COALESCE(maxDBH, defaultMaxDBH); + END IF; + + SET validationCriteria = 'DBH Measurement Range Validation'; + SET measuredValue = CONCAT('Measured DBH: ', (SELECT MeasuredDBH + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID)); + SET expectedValueRange = CONCAT('Expected DBH Range: ', minDBH, ' - ', maxDBH); + SET additionalDetails = 'Checks if the measured DBH falls within the specified minimum and maximum range.'; + + IF (SELECT MeasuredDBH + FROM coremeasurements + WHERE CoreMeasurementID = vCoreMeasurementID + AND ( + (MeasuredDBH < 0) OR + (maxDBH IS NOT NULL AND MeasuredDBH > maxDBH) OR + (minDBH IS NULL AND maxDBH IS NULL) + )) THEN + SET validationResult = 0; + SET errorMessage = CONCAT('DBH outside bounds: ', minDBH, ' - ', maxDBH); + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, MeasuredValue, ExpectedValueRange, + AdditionalDetails) + VALUES ('ValidateScreenMeasuredDiameterMinMax', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, measuredValue, expectedValueRange, + additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + +create + definer = azureroot@`%` procedure ValidateScreenStemsWithMeasurementsButDeadAttributes(IN p_CensusID int, IN p_PlotID int) +BEGIN + DECLARE vCoreMeasurementID INT; + DECLARE validationResult BIT; + DECLARE errorMessage VARCHAR(255); + DECLARE validationCriteria TEXT; + DECLARE additionalDetails TEXT; + DECLARE insertCount INT DEFAULT 0; + DECLARE expectedCount INT; + DECLARE successMessage VARCHAR(255); + DECLARE done INT DEFAULT FALSE; + DECLARE veID INT; + DECLARE vExistingErrorID INT; + + DECLARE cur CURSOR FOR + SELECT cm.CoreMeasurementID + FROM coremeasurements cm + JOIN cmattributes cma ON cm.CoreMeasurementID = cma.CoreMeasurementID + JOIN attributes a ON cma.Code = a.Code + JOIN stems st ON cm.StemID = st.StemID + JOIN quadrats q ON st.QuadratID = q.QuadratID + WHERE ((cm.MeasuredDBH IS NOT NULL AND cm.MeasuredDBH > 0) OR + (cm.MeasuredHOM IS NOT NULL AND cm.MeasuredHOM > 0)) + AND a.Status IN ('dead', 'stem dead', 'missing', 'broken below', 'omitted') + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations + ( + CoreMeasurementID INT + ); + + SELECT COUNT(*) + INTO expectedCount + FROM coremeasurements cm + JOIN cmattributes cma ON cm.CoreMeasurementID = cma.CoreMeasurementID + JOIN attributes a ON cma.Code = a.Code + JOIN stems st ON cm.StemID = st.StemID + JOIN quadrats q ON st.QuadratID = q.QuadratID + WHERE ((cm.MeasuredDBH IS NOT NULL AND cm.MeasuredDBH > 0) OR + (cm.MeasuredHOM IS NOT NULL AND cm.MeasuredHOM > 0)) + AND a.Status IN ('dead', 'stem dead', 'missing', 'broken below', 'omitted') + AND cm.IsValidated IS FALSE + AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) + AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); + + SELECT ValidationID + INTO veID + FROM catalog.validationprocedures + WHERE ProcedureName = 'ValidateScreenStemsWithMeasurementsButDeadAttributes'; + + + OPEN cur; + loop1: + LOOP + FETCH cur INTO vCoreMeasurementID; + IF done THEN + LEAVE loop1; + END IF; + + SET validationCriteria = 'Stem Measurements with Dead Attributes Validation'; + SET additionalDetails = 'Verifies that stems marked as dead do not have active measurements.'; + + IF EXISTS (SELECT 1 + FROM cmattributes cma + JOIN attributes a ON cma.Code = a.Code + JOIN coremeasurements cm on cma.CoreMeasurementID = cm.CoreMeasurementID + WHERE cma.CoreMeasurementID = vCoreMeasurementID + AND a.Status IN ('dead', 'stem dead', 'missing', 'broken below', 'omitted') + AND ((cm.MeasuredDBH IS NOT NULL AND cm.MeasuredDBH > 0) OR + (cm.MeasuredHOM IS NOT NULL AND cm.MeasuredHOM > 0))) THEN + SET validationResult = 0; + SET errorMessage = 'Stem with measurements but dead attributes detected.'; + -- Check if the error record already exists before inserting + IF NOT EXISTS (SELECT 1 + FROM cmverrors + WHERE CoreMeasurementID = vCoreMeasurementID + AND ValidationErrorID = veID) THEN + INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) + VALUES (vCoreMeasurementID, veID); + END IF; + INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); + SET insertCount = insertCount + 1; + ELSE + SET validationResult = 1; + SET errorMessage = NULL; + END IF; + + INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, + ValidationOutcome, ErrorMessage, + ValidationCriteria, AdditionalDetails) + VALUES ('ValidateScreenStemsWithMeasurementsButDeadAttributes', NOW(), vCoreMeasurementID, + IF(validationResult, 'Passed', 'Failed'), errorMessage, + validationCriteria, additionalDetails); + END LOOP; + CLOSE cur; + + SET successMessage = + CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); + SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; + + SELECT CoreMeasurementID FROM FailedValidations; + + DROP TEMPORARY TABLE IF EXISTS FailedValidations; +END; + diff --git a/frontend/sqlscripting/resetschema.sql b/frontend/sqlscripting/resetschema.sql index 07315457..be5b483d 100644 --- a/frontend/sqlscripting/resetschema.sql +++ b/frontend/sqlscripting/resetschema.sql @@ -19,4 +19,21 @@ truncate stems; truncate subquadrats; truncate unifiedchangelog; truncate validationchangelog; +DROP VIEW IF EXISTS `alltaxonomiesview`; +DROP VIEW IF EXISTS `measurementssummaryview`; +DROP VIEW IF EXISTS `stemtaxonomiesview`; +DROP VIEW IF EXISTS `viewfulltableview`; +DROP PROCEDURE IF EXISTS `UpdateValidationStatus`; +DROP PROCEDURE IF EXISTS `ValidateDBHGrowthExceedsMax`; +DROP PROCEDURE IF EXISTS `ValidateDBHShrinkageExceedsMax`; +DROP PROCEDURE IF EXISTS `ValidateFindAllInvalidSpeciesCodes`; +DROP PROCEDURE IF EXISTS `ValidateFindDuplicatedQuadratsByName`; +DROP PROCEDURE IF EXISTS `ValidateFindDuplicateStemTreeTagCombinationsPerCensus`; +DROP PROCEDURE IF EXISTS `ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat`; +DROP PROCEDURE IF EXISTS `ValidateFindStemsInTreeWithDifferentSpecies`; +DROP PROCEDURE IF EXISTS `ValidateFindStemsOutsidePlots`; +DROP PROCEDURE IF EXISTS `ValidateFindTreeStemsInDifferentQuadrats`; +DROP PROCEDURE IF EXISTS `ValidateHOMUpperAndLowerBounds`; +DROP PROCEDURE IF EXISTS `ValidateScreenMeasuredDiameterMinMax`; +DROP PROCEDURE IF EXISTS `ValidateScreenStemsWithMeasurementsButDeadAttributes`; set foreign_key_checks = 1; \ No newline at end of file diff --git a/frontend/sqlscripting/tablestructures.sql b/frontend/sqlscripting/tablestructures.sql index 2c9c381c..7447f5e4 100644 --- a/frontend/sqlscripting/tablestructures.sql +++ b/frontend/sqlscripting/tablestructures.sql @@ -145,7 +145,6 @@ create table species SpeciesID int auto_increment primary key, GenusID int null, - CensusID int null, SpeciesCode varchar(25) null, SpeciesName varchar(64) null, SubspeciesName varchar(255) null, @@ -163,9 +162,7 @@ create table species constraint Species_Genus_GenusID_fk foreign key (GenusID) references genus (GenusID), constraint Species_Reference_ReferenceID_fk - foreign key (ReferenceID) references reference (ReferenceID), - constraint species_census_CensusID_fk - foreign key (CensusID) references census (CensusID) + foreign key (ReferenceID) references reference (ReferenceID) ); create table specieslimits diff --git a/frontend/sqlscripting/updated_triggers_7-3-24.sql b/frontend/sqlscripting/updated_triggers_7-3-24.sql index 5734760b..04846b99 100644 --- a/frontend/sqlscripting/updated_triggers_7-3-24.sql +++ b/frontend/sqlscripting/updated_triggers_7-3-24.sql @@ -44,7 +44,7 @@ BEGIN 'Code', OLD.Code, 'Description', OLD.Description, 'Status', OLD.Status - ); + ); -- Construct the JSON object for the new row SET new_json = JSON_OBJECT( @@ -76,12 +76,12 @@ CREATE TRIGGER after_delete_attributes BEGIN DECLARE old_json JSON; - -- Construct the JSON object for the new row + -- Construct the JSON object for the new row SET old_json = JSON_OBJECT( 'Code', OLD.Code, 'Description', OLD.Description, 'Status', OLD.Status - ); + ); -- Insert the change log entry into unifiedchangelog INSERT INTO unifiedchangelog (TableName, RecordID, Operation, OldRowState, ChangeTimestamp, ChangedBy) @@ -1138,7 +1138,6 @@ BEGIN SET new_json = JSON_OBJECT( 'SpeciesID', NEW.SpeciesID, 'GenusID', NEW.GenusID, - 'CensusID', NEW.CensusID, 'SpeciesCode', NEW.SpeciesCode, 'SpeciesName', NEW.SpeciesName, 'SubspeciesName', NEW.SubspeciesName, @@ -1179,7 +1178,6 @@ BEGIN SET old_json = JSON_OBJECT( 'SpeciesID', OLD.SpeciesID, 'GenusID', OLD.GenusID, - 'CensusID', OLD.CensusID, 'SpeciesCode', OLD.SpeciesCode, 'SpeciesName', OLD.SpeciesName, 'SubspeciesName', OLD.SubspeciesName, @@ -1196,7 +1194,6 @@ BEGIN SET new_json = JSON_OBJECT( 'SpeciesID', NEW.SpeciesID, 'GenusID', NEW.GenusID, - 'CensusID', NEW.CensusID, 'SpeciesCode', NEW.SpeciesCode, 'SpeciesName', NEW.SpeciesName, 'SubspeciesName', NEW.SubspeciesName, @@ -1237,7 +1234,6 @@ BEGIN SET old_json = JSON_OBJECT( 'SpeciesID', OLD.SpeciesID, 'GenusID', OLD.GenusID, - 'CensusID', OLD.CensusID, 'SpeciesCode', OLD.SpeciesCode, 'SpeciesName', OLD.SpeciesName, 'SubspeciesName', OLD.SubspeciesName, @@ -1394,8 +1390,8 @@ BEGIN 'DimensionX', NEW.DimensionX, 'DimensionY', NEW.DimensionY, 'DimensionUnits', NEW.DimensionUnits, - 'X', NEW.X, - 'Y', NEW.Y, + 'QX', NEW.QX, + 'QY', NEW.QY, 'CoordinateUnits', NEW.CoordinateUnits, 'Ordering', NEW.Ordering ); @@ -1432,8 +1428,8 @@ BEGIN 'DimensionX', OLD.DimensionX, 'DimensionY', OLD.DimensionY, 'DimensionUnits', OLD.DimensionUnits, - 'X', OLD.X, - 'Y', OLD.Y, + 'QX', OLD.QX, + 'QY', OLD.QY, 'CoordinateUnits', OLD.CoordinateUnits, 'Ordering', OLD.Ordering ); @@ -1446,8 +1442,8 @@ BEGIN 'DimensionX', NEW.DimensionX, 'DimensionY', NEW.DimensionY, 'DimensionUnits', NEW.DimensionUnits, - 'X', NEW.X, - 'Y', NEW.Y, + 'QX', NEW.QX, + 'QY', NEW.QY, 'CoordinateUnits', NEW.CoordinateUnits, 'Ordering', NEW.Ordering ); @@ -1483,8 +1479,8 @@ BEGIN 'DimensionX', OLD.DimensionX, 'DimensionY', OLD.DimensionY, 'DimensionUnits', OLD.DimensionUnits, - 'X', OLD.X, - 'Y', OLD.Y, + 'QX', OLD.QX, + 'QY', OLD.QY, 'CoordinateUnits', OLD.CoordinateUnits, 'Ordering', OLD.Ordering ); diff --git a/frontend/sqlscripting/updatedviews.sql b/frontend/sqlscripting/updatedviews.sql new file mode 100644 index 00000000..825d7e2f --- /dev/null +++ b/frontend/sqlscripting/updatedviews.sql @@ -0,0 +1,195 @@ +CREATE VIEW alltaxonomiesview AS +SELECT + s.SpeciesID AS SpeciesID, + f.FamilyID AS FamilyID, + g.GenusID AS GenusID, + r.ReferenceID AS ReferenceID, + s.SpeciesCode AS SpeciesCode, + f.Family AS Family, + g.Genus AS Genus, + g.GenusAuthority AS GenusAuthority, + s.SpeciesName AS SpeciesName, + s.SubspeciesName AS SubSpeciesName, + s.IDLevel AS SpeciesIDLevel, + s.SpeciesAuthority AS SpeciesAuthority, + s.SubspeciesAuthority AS SubspeciesAuthority, + s.ValidCode AS ValidCode, + s.FieldFamily AS FieldFamily, + s.Description AS SpeciesDescription, + r.PublicationTitle AS PublicationTitle, + r.FullReference AS FullReference, + r.DateOfPublication AS DateOfPublication, + r.Citation AS Citation +FROM + family f + JOIN genus g ON f.FamilyID = g.FamilyID + JOIN species s ON g.GenusID = s.GenusID + LEFT JOIN reference r ON s.ReferenceID = r.ReferenceID; + + +CREATE VIEW measurementssummaryview AS +SELECT + cm.CoreMeasurementID AS CoreMeasurementID, + p.PlotID AS PlotID, + cm.CensusID AS CensusID, + q.QuadratID AS QuadratID, + s.SpeciesID AS SpeciesID, + t.TreeID AS TreeID, + st.StemID AS StemID, + qp.PersonnelID AS PersonnelID, + p.PlotName AS PlotName, + q.QuadratName AS QuadratName, + s.SpeciesCode AS SpeciesCode, + t.TreeTag AS TreeTag, + st.StemTag AS StemTag, + st.LocalX AS StemLocalX, + st.LocalY AS StemLocalY, + st.CoordinateUnits AS StemUnits, + COALESCE(CONCAT(pe.FirstName, ' ', pe.LastName), 'Unknown') AS PersonnelName, + cm.MeasurementDate AS MeasurementDate, + cm.MeasuredDBH AS MeasuredDBH, + cm.DBHUnit AS DBHUnits, + cm.MeasuredHOM AS MeasuredHOM, + cm.HOMUnit AS HOMUnits, + cm.IsValidated AS IsValidated, + cm.Description AS Description, + (SELECT GROUP_CONCAT(ca.Code SEPARATOR '; ') + FROM cmattributes ca + WHERE ca.CoreMeasurementID = cm.CoreMeasurementID) AS Attributes +FROM + coremeasurements cm + LEFT JOIN stems st ON cm.StemID = st.StemID + LEFT JOIN trees t ON st.TreeID = t.TreeID + LEFT JOIN species s ON t.SpeciesID = s.SpeciesID + LEFT JOIN genus g ON s.GenusID = g.GenusID + LEFT JOIN family f ON g.FamilyID = f.FamilyID + LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID + LEFT JOIN plots p ON q.PlotID = p.PlotID + LEFT JOIN census c ON cm.CensusID = c.CensusID + LEFT JOIN quadratpersonnel qp ON q.QuadratID = qp.QuadratID + LEFT JOIN personnel pe ON qp.PersonnelID = pe.PersonnelID +GROUP BY + cm.CoreMeasurementID, p.PlotID, cm.CensusID, q.QuadratID, s.SpeciesID, t.TreeID, st.StemID, qp.PersonnelID, + p.PlotName, q.QuadratName, s.SpeciesCode, t.TreeTag, st.StemTag, st.LocalX, st.LocalY, st.CoordinateUnits, + pe.FirstName, pe.LastName, cm.MeasurementDate, cm.MeasuredDBH, cm.DBHUnit, cm.MeasuredHOM, cm.HOMUnit, + cm.IsValidated, cm.Description; + + +CREATE VIEW stemtaxonomiesview AS +SELECT + s.StemID AS StemID, + t.TreeID AS TreeID, + q.QuadratID AS QuadratID, + c.CensusID AS CensusID, + p.PlotID AS PlotID, + f.FamilyID AS FamilyID, + g.GenusID AS GenusID, + sp.SpeciesID AS SpeciesID, + s.StemTag AS StemTag, + t.TreeTag AS TreeTag, + sp.SpeciesCode AS SpeciesCode, + f.Family AS Family, + g.Genus AS Genus, + sp.SpeciesName AS SpeciesName, + sp.SubspeciesName AS SubspeciesName, + sp.ValidCode AS ValidCode, + g.GenusAuthority AS GenusAuthority, + sp.SpeciesAuthority AS SpeciesAuthority, + sp.SubspeciesAuthority AS SubspeciesAuthority, + sp.IDLevel AS SpeciesIDLevel, + sp.FieldFamily AS SpeciesFieldFamily +FROM + stems s + JOIN trees t ON s.TreeID = t.TreeID + JOIN quadrats q ON s.QuadratID = q.QuadratID + JOIN census c ON q.CensusID = c.CensusID + JOIN plots p ON c.PlotID = p.PlotID + JOIN species sp ON t.SpeciesID = sp.SpeciesID + JOIN genus g ON sp.GenusID = g.GenusID + LEFT JOIN family f ON g.FamilyID = f.FamilyID; + + +CREATE VIEW viewfulltableview AS +SELECT + cm.CoreMeasurementID AS CoreMeasurementID, + cm.MeasurementDate AS MeasurementDate, + cm.MeasuredDBH AS MeasuredDBH, + cm.DBHUnit AS DBHUnits, + cm.MeasuredHOM AS MeasuredHOM, + cm.HOMUnit AS HOMUnits, + cm.Description AS Description, + cm.IsValidated AS IsValidated, + p.PlotID AS PlotID, + p.PlotName AS PlotName, + p.LocationName AS LocationName, + p.CountryName AS CountryName, + p.DimensionX AS DimensionX, + p.DimensionY AS DimensionY, + p.Area AS PlotArea, + p.GlobalX AS GlobalX, + p.GlobalY AS GlobalY, + p.GlobalZ AS GlobalZ, + p.DimensionUnits AS PlotUnit, + p.PlotShape AS PlotShape, + p.PlotDescription AS PlotDescription, + c.CensusID AS CensusID, + c.StartDate AS CensusStartDate, + c.EndDate AS CensusEndDate, + c.Description AS CensusDescription, + c.PlotCensusNumber AS PlotCensusNumber, + q.QuadratID AS QuadratID, + q.QuadratName AS QuadratName, + q.DimensionX AS QuadratDimensionX, + q.DimensionY AS QuadratDimensionY, + q.Area AS QuadratArea, + q.QuadratShape AS QuadratShape, + q.DimensionUnits AS QuadratUnit, + sq.SubquadratID AS SubquadratID, + sq.SubquadratName AS SubquadratName, + sq.DimensionX AS SubquadratDimensionX, + sq.DimensionY AS SubquadratDimensionY, + sq.QX AS QX, + sq.QY AS QY, + sq.CoordinateUnits AS SubquadratUnit, + t.TreeID AS TreeID, + t.TreeTag AS TreeTag, + s.StemID AS StemID, + s.StemTag AS StemTag, + s.LocalX AS StemLocalX, + s.LocalY AS StemLocalY, + s.CoordinateUnits AS StemUnits, + per.PersonnelID AS PersonnelID, + per.FirstName AS FirstName, + per.LastName AS LastName, + r.RoleName AS PersonnelRoles, + sp.SpeciesID AS SpeciesID, + sp.SpeciesCode AS SpeciesCode, + sp.SpeciesName AS SpeciesName, + sp.SubspeciesName AS SubspeciesName, + sp.SubspeciesAuthority AS SubspeciesAuthority, + sp.IDLevel AS SpeciesIDLevel, + g.GenusID AS GenusID, + g.Genus AS Genus, + g.GenusAuthority AS GenusAuthority, + fam.FamilyID AS FamilyID, + fam.Family AS Family, + attr.Code AS AttributeCode, + attr.Description AS AttributeDescription, + attr.Status AS AttributeStatus +FROM + coremeasurements cm + LEFT JOIN stems s ON cm.StemID = s.StemID + LEFT JOIN trees t ON s.TreeID = t.TreeID + LEFT JOIN species sp ON t.SpeciesID = sp.SpeciesID + LEFT JOIN genus g ON sp.GenusID = g.GenusID + LEFT JOIN family fam ON g.FamilyID = fam.FamilyID + LEFT JOIN specieslimits sl ON sp.SpeciesCode = sl.SpeciesCode + LEFT JOIN quadrats q ON s.QuadratID = q.QuadratID + LEFT JOIN quadratpersonnel qp ON q.QuadratID = qp.QuadratID + LEFT JOIN personnel per ON qp.PersonnelID = per.PersonnelID + LEFT JOIN plots p ON q.PlotID = p.PlotID + LEFT JOIN subquadrats sq ON q.QuadratID = sq.QuadratID + LEFT JOIN census c ON cm.CensusID = c.CensusID + LEFT JOIN roles r ON per.RoleID = r.RoleID + LEFT JOIN attributes attr ON cm.CoreMeasurementID = attr.Code + LEFT JOIN cmattributes cma ON cm.CoreMeasurementID = cma.CoreMeasurementID AND attr.Code = cma.Code; diff --git a/frontend/sqlscripting/views.sql b/frontend/sqlscripting/views.sql deleted file mode 100644 index 6c04c8d2..00000000 --- a/frontend/sqlscripting/views.sql +++ /dev/null @@ -1,1512 +0,0 @@ -create - definer = azureroot@`%` procedure UpdateValidationStatus(IN p_PlotID int, IN p_CensusID int, OUT RowsValidated int) -BEGIN - -- Create a temporary table to store CoreMeasurementIDs - CREATE TEMPORARY TABLE IF NOT EXISTS TempUpdatedIDs (CoreMeasurementID INT); - - -- Clear the temporary table - TRUNCATE TABLE TempUpdatedIDs; - - -- Insert the CoreMeasurementIDs of the rows to be updated into the temporary table - INSERT INTO TempUpdatedIDs (CoreMeasurementID) - SELECT cm.CoreMeasurementID - FROM coremeasurements cm - LEFT JOIN cmverrors cme ON cm.CoreMeasurementID = cme.CoreMeasurementID - LEFT JOIN stems s on cm.StemID = s.StemID - LEFT JOIN quadrats q on s.QuadratID = q.QuadratID - WHERE cm.IsValidated = FALSE - AND (q.PlotID = p_PlotID OR p_PlotID IS NULL) - AND (q.CensusID = p_CensusID OR p_CensusID IS NULL) - AND cme.CoreMeasurementID IS NULL; - - -- Update the IsValidated column - UPDATE coremeasurements cm - INNER JOIN TempUpdatedIDs tmp ON cm.CoreMeasurementID = tmp.CoreMeasurementID - SET cm.IsValidated = TRUE; - - -- Get the count of rows that have been updated - SET RowsValidated = ROW_COUNT(); - - -- Select the CoreMeasurementIDs from the temporary table - SELECT CoreMeasurementID FROM TempUpdatedIDs; - - -- Optionally, drop the temporary table - DROP TEMPORARY TABLE IF EXISTS TempUpdatedIDs; -END; - -create - definer = azureroot@`%` procedure ValidateDBHGrowthExceedsMax(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE vPrevDBH DECIMAL(10, 2); - DECLARE vCurrDBH DECIMAL(10, 2); - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE done INT DEFAULT FALSE; - DECLARE veID INT; - DECLARE cur CURSOR FOR - SELECT cm2.CoreMeasurementID, cm1.MeasuredDBH, cm2.MeasuredDBH - FROM coremeasurements cm1 - JOIN coremeasurements cm2 - ON cm1.StemID = cm2.StemID - AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 - LEFT JOIN stems st2 ON cm2.StemID = st2.StemID - LEFT JOIN quadrats q ON st2.QuadratID = q.QuadratID - LEFT JOIN cmattributes cma - ON cm1.CoreMeasurementID = cma.CoreMeasurementID - LEFT JOIN attributes a - ON cma.Code = a.Code - WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) - AND cm1.MeasuredDBH IS NOT NULL - AND cm2.MeasuredDBH IS NOT NULL - AND (cm2.MeasuredDBH - cm1.MeasuredDBH > 65) - AND cm1.IsValidated IS TRUE - AND cm2.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm1 - JOIN coremeasurements cm2 - ON cm1.StemID = cm2.StemID - AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 - LEFT JOIN stems st2 ON cm2.StemID = st2.StemID - LEFT JOIN quadrats q ON st2.QuadratID = q.QuadratID - LEFT JOIN cmattributes cma - ON cm1.CoreMeasurementID = cma.CoreMeasurementID - LEFT JOIN attributes a - ON cma.Code = a.Code - WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) - AND cm1.MeasuredDBH IS NOT NULL - AND cm2.MeasuredDBH IS NOT NULL - AND (cm2.MeasuredDBH - cm1.MeasuredDBH > 65) - AND cm1.IsValidated IS TRUE - AND cm2.IsValidated IS FALSE - AND (p_CensusID = -1 OR q.CensusID = p_CensusID) - AND (p_PlotID = -1 OR q.PlotID = p_PlotID); - - -- Fetch the ValidationErrorID for this stored procedure - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateDBHGrowthExceedsMax'; - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID, vPrevDBH, vCurrDBH; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Annual DBH Growth'; - SET measuredValue = CONCAT('Previous DBH: ', vPrevDBH, ', Current DBH: ', vCurrDBH); - SET expectedValueRange = 'Growth <= 65'; - SET additionalDetails = 'Checked for excessive DBH growth over a year'; - - IF vCurrDBH - vPrevDBH > 65 THEN - SET validationResult = 0; - SET errorMessage = 'Growth exceeds max threshold.'; - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateDBHGrowthExceedsMax', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, - insertCount AS FailedRows, - successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateDBHShrinkageExceedsMax(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE vPrevDBH DECIMAL(10, 2); - DECLARE vCurrDBH DECIMAL(10, 2); - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - DECLARE cur CURSOR FOR - SELECT cm2.CoreMeasurementID, cm1.MeasuredDBH, cm2.MeasuredDBH - FROM coremeasurements cm1 - JOIN coremeasurements cm2 - ON cm1.StemID = cm2.StemID - AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 - LEFT JOIN stems st ON cm2.StemID = st.StemID - LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID - LEFT JOIN cmattributes cma - ON cm1.CoreMeasurementID = cma.CoreMeasurementID - LEFT JOIN attributes a - ON cma.Code = a.Code - WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) - AND cm1.MeasuredDBH IS NOT NULL - AND cm2.MeasuredDBH IS NOT NULL - AND cm1.IsValidated IS TRUE - AND cm2.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm1 - JOIN coremeasurements cm2 - ON cm1.StemID = cm2.StemID - AND YEAR(cm2.MeasurementDate) = YEAR(cm1.MeasurementDate) + 1 - LEFT JOIN stems st ON cm2.StemID = st.StemID - LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID - LEFT JOIN cmattributes cma - ON cm1.CoreMeasurementID = cma.CoreMeasurementID - LEFT JOIN attributes a - ON cma.Code = a.Code - WHERE (a.Status NOT IN ('dead', 'stem dead', 'broken below', 'missing', 'omitted') OR a.Status IS NULL) - AND cm1.MeasuredDBH IS NOT NULL - AND cm2.MeasuredDBH IS NOT NULL - AND cm1.IsValidated IS TRUE - AND cm2.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateDBHShrinkageExceedsMax'; - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID, vPrevDBH, vCurrDBH; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Annual DBH Shrinkage'; - SET measuredValue = CONCAT('Previous DBH: ', vPrevDBH, ', Current DBH: ', vCurrDBH); - SET expectedValueRange = 'Shrinkage < 5% of previous DBH'; - SET additionalDetails = 'Checked for excessive DBH shrinkage over a year'; - - IF vCurrDBH < vPrevDBH * 0.95 THEN - SET validationResult = 0; - SET errorMessage = 'Shrinkage exceeds maximum allowed threshold.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateDBHShrinkageExceedsMax', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindAllInvalidSpeciesCodes(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE vSpeciesID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID, sp.SpeciesID - FROM stems s - JOIN trees t ON s.TreeID = t.TreeID - LEFT JOIN species sp ON t.SpeciesID = sp.SpeciesID - JOIN coremeasurements cm ON s.StemID = cm.StemID - LEFT JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE sp.SpeciesID IS NULL - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY cm.CoreMeasurementID; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM stems s - JOIN trees t ON s.TreeID = t.TreeID - LEFT JOIN species sp ON t.SpeciesID = sp.SpeciesID - JOIN coremeasurements cm ON s.StemID = cm.StemID - LEFT JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE sp.SpeciesID IS NULL - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY cm.CoreMeasurementID; - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindAllInvalidSpeciesCodes'; - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID, vSpeciesID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Species Code Validation'; - SET measuredValue = CONCAT('Species ID: ', IFNULL(vSpeciesID, 'NULL')); - SET expectedValueRange = 'Non-null and valid Species ID'; - SET additionalDetails = 'Checking for the existence of valid species codes for each measurement.'; - - IF vSpeciesID IS NULL THEN - SET validationResult = 0; - SET errorMessage = 'Invalid species code detected.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindAllInvalidSpeciesCodes', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindDuplicateStemTreeTagCombinationsPerCensus(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - DECLARE cur CURSOR FOR - SELECT SubQuery.CoreMeasurementID - FROM (SELECT cm.CoreMeasurementID - FROM coremeasurements cm - INNER JOIN stems s ON cm.StemID = s.StemID - INNER JOIN trees t ON s.TreeID = t.TreeID - INNER JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - AND cm.IsValidated = FALSE - GROUP BY q.CensusID, s.StemTag, t.TreeTag, cm.CoreMeasurementID - HAVING COUNT(*) > 1) AS SubQuery; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM (SELECT cm.CoreMeasurementID - FROM coremeasurements cm - INNER JOIN stems s ON cm.StemID = s.StemID - INNER JOIN trees t ON s.TreeID = t.TreeID - INNER JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - AND cm.IsValidated = FALSE - GROUP BY q.CensusID, s.StemTag, t.TreeTag, cm.CoreMeasurementID - HAVING COUNT(*) > 1) AS DuplicationCheck; - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindDuplicateStemTreeTagCombinationsPerCensus'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Duplicate Stem-Tree Tag Combinations per Census'; - SET measuredValue = 'N/A'; - SET expectedValueRange = 'Unique Stem-Tree Tag Combinations'; - SET additionalDetails = 'Checking for duplicate stem and tree tag combinations in each census.'; - - IF EXISTS (SELECT 1 - FROM coremeasurements cm - INNER JOIN stems s ON cm.StemID = s.StemID - INNER JOIN trees t ON s.TreeID = t.TreeID - INNER JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE cm.CoreMeasurementID = vCoreMeasurementID - GROUP BY q.CensusID, s.StemTag, t.TreeTag - HAVING COUNT(cm.CoreMeasurementID) > 1) THEN - SET validationResult = 0; - SET errorMessage = 'Duplicate stem and tree tag combination detected.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindDuplicateStemTreeTagCombinationsPerCensus', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindDuplicatedQuadratsByName(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID - FROM quadrats q - LEFT JOIN stems st ON q.QuadratID = st.QuadratID - JOIN coremeasurements cm ON st.StemID = cm.StemID - WHERE cm.IsValidated IS FALSE - AND (q.PlotID, q.QuadratName) IN (SELECT PlotID, QuadratName - FROM quadrats - GROUP BY PlotID, QuadratName - HAVING COUNT(*) > 1) - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY cm.CoreMeasurementID; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM quadrats q - LEFT JOIN stems st ON q.QuadratID = st.QuadratID - JOIN coremeasurements cm ON st.StemID = cm.StemID - WHERE cm.IsValidated IS FALSE - AND (q.PlotID, q.QuadratName) IN (SELECT PlotID, QuadratName - FROM quadrats - GROUP BY PlotID, QuadratName - HAVING COUNT(*) > 1) - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY cm.CoreMeasurementID; - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindDuplicatedQuadratsByName'; - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Quadrat Name Duplication'; - SET measuredValue = 'N/A'; - SET expectedValueRange = 'Unique Quadrat Names per Plot'; - SET additionalDetails = 'Checking for duplicated quadrat names within the same plot.'; - - IF EXISTS (SELECT 1 - FROM quadrats q - WHERE q.QuadratID = vCoreMeasurementID - AND (q.PlotID, q.QuadratName) IN (SELECT PlotID, QuadratName - FROM quadrats - GROUP BY PlotID, QuadratName - HAVING COUNT(*) > 1)) THEN - SET validationResult = 0; - SET errorMessage = 'Duplicated quadrat name detected.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindDuplicatedQuadratsByName', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - DECLARE cur CURSOR FOR - SELECT MIN(cm.CoreMeasurementID) AS CoreMeasurementID - FROM coremeasurements cm - JOIN stems st ON cm.StemID = st.StemID - JOIN quadrats q ON st.QuadratID = q.QuadratID - JOIN census c ON q.CensusID = c.CensusID - WHERE (cm.MeasurementDate < c.StartDate OR cm.MeasurementDate > c.EndDate) - AND cm.MeasurementDate IS NOT NULL - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR c.PlotID = p_PlotID) - GROUP BY q.QuadratID, c.CensusID, c.StartDate, c.EndDate; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm - JOIN stems st ON cm.StemID = st.StemID - JOIN quadrats q ON st.QuadratID = q.QuadratID - JOIN census c ON q.CensusID = c.CensusID - WHERE (cm.MeasurementDate < c.StartDate OR cm.MeasurementDate > c.EndDate) - AND cm.MeasurementDate IS NOT NULL - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR c.PlotID = p_PlotID); - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Measurement Date vs Census Date Bounds'; - SET measuredValue = 'Measurement Date'; - SET expectedValueRange = 'Within Census Start and End Dates'; - SET additionalDetails = - 'Checking if measurement dates fall within the start and end dates of their respective censuses.'; - - IF EXISTS (SELECT 1 - FROM coremeasurements cm - JOIN stems st ON cm.StemID = st.StemID - JOIN quadrats q ON st.QuadratID = q.QuadratID - JOIN census c ON q.CensusID = c.CensusID - WHERE cm.CoreMeasurementID = vCoreMeasurementID - AND (cm.MeasurementDate < c.StartDate OR cm.MeasurementDate > c.EndDate)) THEN - SET validationResult = 0; - SET errorMessage = 'Measurement outside census date bounds.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindMeasurementsOutsideCensusDateBoundsGroupByQuadrat', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindStemsInTreeWithDifferentSpecies(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID - FROM coremeasurements cm - JOIN stems s ON cm.StemID = s.StemID - JOIN trees t ON s.TreeID = t.TreeID - JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE cm.IsValidated = FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY t.TreeID, cm.CoreMeasurementID - HAVING COUNT(DISTINCT t.SpeciesID) > 1; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm - JOIN stems s ON cm.StemID = s.StemID - JOIN trees t ON s.TreeID = t.TreeID - JOIN quadrats q ON s.QuadratID = q.QuadratID - WHERE cm.IsValidated = FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY t.TreeID - HAVING COUNT(DISTINCT t.SpeciesID) > 1; - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindStemsInTreeWithDifferentSpecies'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Each tree should have a consistent species across all its stems.'; - SET measuredValue = 'Species consistency across tree stems'; - SET expectedValueRange = 'One species per tree'; - SET additionalDetails = 'Checking if stems belonging to the same tree have different species IDs.'; - - IF EXISTS (SELECT 1 - FROM stems s - JOIN trees t ON s.TreeID = t.TreeID - WHERE t.TreeID IN (SELECT TreeID - FROM stems - WHERE StemID IN - (SELECT StemID - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID)) - GROUP BY t.TreeID - HAVING COUNT(DISTINCT t.SpeciesID) > 1) THEN - SET validationResult = 0; - SET errorMessage = 'Stems in the same tree have different species.'; - - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindStemsInTreeWithDifferentSpecies', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = CONCAT('Validation completed. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindStemsOutsidePlots(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID - FROM stems s - INNER JOIN coremeasurements cm ON s.StemID = cm.StemID - INNER JOIN quadrats q ON s.QuadratID = q.QuadratID - INNER JOIN plots p ON q.PlotID = p.PlotID - WHERE (s.LocalX > p.DimensionX OR s.LocalX > p.DimensionY) - AND s.LocalX IS NOT NULL - AND s.LocalY IS NOT NULL - AND (p.DimensionX > 0 AND p.DimensionY > 0) - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID) - GROUP BY cm.CoreMeasurementID; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM stems s - INNER JOIN quadrats q ON s.QuadratID = q.QuadratID - INNER JOIN plots p ON q.PlotID = p.PlotID - INNER JOIN coremeasurements cm ON s.StemID = cm.StemID - WHERE (s.LocalX > p.DimensionX OR s.LocalX > p.DimensionY) - AND s.LocalX IS NOT NULL - AND s.LocalY IS NOT NULL - AND (p.DimensionX > 0 AND p.DimensionY > 0) - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindStemsOutsidePlots'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Stem Placement within Plot Boundaries'; - SET measuredValue = 'Stem Plot Coordinates'; - SET expectedValueRange = 'Within Plot Dimensions'; - SET additionalDetails = 'Validating whether stems are located within the specified plot dimensions.'; - - IF EXISTS (SELECT 1 - FROM stems s - INNER JOIN quadrats q ON s.QuadratID = q.QuadratID - INNER JOIN plots p ON q.PlotID = p.PlotID - WHERE s.StemID IN - (SELECT StemID - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID) - AND (s.LocalX > p.DimensionX OR s.LocalY > p.DimensionY)) THEN - SET validationResult = 0; - SET errorMessage = 'Stem is outside plot dimensions.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindStemsOutsidePlots', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = CONCAT('Validation completed. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateFindTreeStemsInDifferentQuadrats(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - - DECLARE cur CURSOR FOR - SELECT cm1.CoreMeasurementID - FROM stems s1 - JOIN stems s2 ON s1.TreeID = s2.TreeID AND s1.StemID != s2.StemID - JOIN quadrats q1 ON s1.QuadratID = q1.QuadratID - JOIN quadrats q2 ON s2.QuadratID = q2.QuadratID - JOIN coremeasurements cm1 ON s1.StemID = cm1.StemID - WHERE q1.QuadratID != q2.QuadratID - AND cm1.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q1.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q1.PlotID = p_PlotID) - GROUP BY cm1.CoreMeasurementID; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - IF p_CensusID IS NULL THEN - SET p_CensusID = -1; - END IF; - IF p_PlotID IS NULL THEN - SET p_PlotID = -1; - END IF; - - SELECT COUNT(*) - INTO expectedCount - FROM stems s1 - JOIN stems s2 ON s1.TreeID = s2.TreeID AND s1.StemID != s2.StemID - JOIN quadrats q1 ON s1.QuadratID = q1.QuadratID - JOIN quadrats q2 ON s2.QuadratID = q2.QuadratID - JOIN coremeasurements cm1 ON s1.StemID = cm1.StemID - WHERE q1.QuadratID != q2.QuadratID - AND cm1.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q1.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q1.PlotID = p_PlotID) - GROUP BY cm1.CoreMeasurementID; - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateFindTreeStemsInDifferentQuadrats'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Stem Quadrat Consistency within Trees'; - SET measuredValue = 'Quadrat IDs of Stems'; - SET expectedValueRange = 'Consistent Quadrat IDs for all Stems in a Tree'; - SET additionalDetails = 'Validating that all stems within the same tree are located in the same quadrat.'; - - IF EXISTS (SELECT 1 - FROM stems s1 - JOIN stems s2 ON s1.TreeID = s2.TreeID AND s1.StemID != s2.StemID - JOIN quadrats q1 on q1.QuadratID = s2.QuadratID - JOIN quadrats q2 on q2.QuadratID = s2.QuadratID - WHERE s1.StemID IN - (SELECT StemID - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID) - AND q1.QuadratID != q2.QuadratID) THEN - SET validationResult = 0; - SET errorMessage = 'Stems in the same tree are in different quadrats.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateFindTreeStemsInDifferentQuadrats', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = CONCAT('Validation completed. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateHOMUpperAndLowerBounds(IN p_CensusID int, IN p_PlotID int, - IN minHOM decimal(10, 2), IN maxHOM decimal(10, 2)) -BEGIN - DECLARE defaultMinHOM DECIMAL(10, 2); - DECLARE defaultMaxHOM DECIMAL(10, 2); - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID - FROM coremeasurements cm - LEFT JOIN stems st ON cm.StemID = st.StemID - LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID - WHERE ( - (minHOM IS NOT NULL AND MeasuredHOM < minHOM) OR - (maxHOM IS NOT NULL AND MeasuredHOM > maxHOM) OR - (minHOM IS NULL AND maxHOM IS NULL) - ) - AND IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm - LEFT JOIN stems st ON cm.StemID = st.StemID - LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID - WHERE ( - (minHOM IS NOT NULL AND MeasuredHOM < minHOM) OR - (maxHOM IS NOT NULL AND MeasuredHOM > maxHOM) OR - (minHOM IS NULL AND maxHOM IS NULL) - ) - AND IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateHOMUpperAndLowerBounds'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - - IF done THEN - LEAVE loop1; - END IF; - - IF minHOM IS NULL OR maxHOM IS NULL THEN - SELECT COALESCE(sl.LowerBound, 0) AS defaultMinHOM, - COALESCE(sl.UpperBound, 9999) AS defaultMaxHOM - INTO defaultMinHOM, defaultMaxHOM - FROM specieslimits sl - JOIN species s ON sl.SpeciesCode = s.SpeciesCode - JOIN trees t ON s.SpeciesID = t.SpeciesID - JOIN stems st ON t.TreeID = st.TreeID - JOIN coremeasurements cm ON st.StemID = cm.StemID - WHERE cm.CoreMeasurementID = vCoreMeasurementID - AND sl.LimitType = 'HOM'; - - SET minHOM = COALESCE(minHOM, defaultMinHOM); - SET maxHOM = COALESCE(maxHOM, defaultMaxHOM); - END IF; - - SET validationCriteria = 'HOM Measurement Range Validation'; - SET measuredValue = CONCAT('Measured HOM: ', (SELECT MeasuredHOM - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID)); - SET expectedValueRange = CONCAT('Expected HOM Range: ', minHOM, ' - ', maxHOM); - SET additionalDetails = 'Checks if the measured HOM falls within the specified minimum and maximum range.'; - - IF (SELECT MeasuredHOM - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID - AND ( - (minHOM IS NOT NULL AND MeasuredHOM < minHOM) OR - (maxHOM IS NOT NULL AND MeasuredHOM > maxHOM) OR - (minHOM IS NULL AND maxHOM IS NULL) - )) THEN - SET validationResult = 0; - SET errorMessage = CONCAT('HOM outside bounds: ', minHOM, ' - ', maxHOM); - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateHOMUpperAndLowerBounds', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateScreenMeasuredDiameterMinMax(IN p_CensusID int, IN p_PlotID int, - IN minDBH decimal(10, 2), - IN maxDBH decimal(10, 2)) -BEGIN - DECLARE defaultMinDBH DECIMAL(10, 2); - DECLARE defaultMaxDBH DECIMAL(10, 2); - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE measuredValue VARCHAR(255); - DECLARE expectedValueRange VARCHAR(255); - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE veID INT; - DECLARE done INT DEFAULT FALSE; - - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID - FROM coremeasurements cm - LEFT JOIN stems st ON cm.StemID = st.StemID - LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID - WHERE ( - (MeasuredDBH < 0) OR - (maxDBH IS NOT NULL AND MeasuredDBH > maxDBH) OR - (minDBH IS NULL AND maxDBH IS NULL) - ) - AND IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm - LEFT JOIN stems st ON cm.StemID = st.StemID - LEFT JOIN quadrats q ON st.QuadratID = q.QuadratID - WHERE ( - (MeasuredDBH < 0) OR - (maxDBH IS NOT NULL AND MeasuredDBH > maxDBH) OR - (minDBH IS NULL AND maxDBH IS NULL) - ) - AND IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateScreenMeasuredDiameterMinMax'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - - IF done THEN - LEAVE loop1; - END IF; - - IF minDBH IS NULL OR maxDBH IS NULL THEN - SELECT COALESCE(sl.LowerBound, 0) AS defaultMinDBH, - COALESCE(sl.UpperBound, 9999) AS defaultMaxDBH - INTO defaultMinDBH, defaultMaxDBH - FROM specieslimits sl - JOIN species s ON sl.SpeciesCode = s.SpeciesCode - JOIN trees t ON s.SpeciesID = t.SpeciesID - JOIN stems st ON t.TreeID = st.TreeID - JOIN coremeasurements cm ON st.StemID = cm.StemID - WHERE cm.CoreMeasurementID = vCoreMeasurementID - AND sl.LimitType = 'DBH'; - - SET minDBH = COALESCE(minDBH, defaultMinDBH); - SET maxDBH = COALESCE(maxDBH, defaultMaxDBH); - END IF; - - SET validationCriteria = 'DBH Measurement Range Validation'; - SET measuredValue = CONCAT('Measured DBH: ', (SELECT MeasuredDBH - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID)); - SET expectedValueRange = CONCAT('Expected DBH Range: ', minDBH, ' - ', maxDBH); - SET additionalDetails = 'Checks if the measured DBH falls within the specified minimum and maximum range.'; - - IF (SELECT MeasuredDBH - FROM coremeasurements - WHERE CoreMeasurementID = vCoreMeasurementID - AND ( - (MeasuredDBH < 0) OR - (maxDBH IS NOT NULL AND MeasuredDBH > maxDBH) OR - (minDBH IS NULL AND maxDBH IS NULL) - )) THEN - SET validationResult = 0; - SET errorMessage = CONCAT('DBH outside bounds: ', minDBH, ' - ', maxDBH); - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, MeasuredValue, ExpectedValueRange, - AdditionalDetails) - VALUES ('ValidateScreenMeasuredDiameterMinMax', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, measuredValue, expectedValueRange, - additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; - -create - definer = azureroot@`%` procedure ValidateScreenStemsWithMeasurementsButDeadAttributes(IN p_CensusID int, IN p_PlotID int) -BEGIN - DECLARE vCoreMeasurementID INT; - DECLARE validationResult BIT; - DECLARE errorMessage VARCHAR(255); - DECLARE validationCriteria TEXT; - DECLARE additionalDetails TEXT; - DECLARE insertCount INT DEFAULT 0; - DECLARE expectedCount INT; - DECLARE successMessage VARCHAR(255); - DECLARE done INT DEFAULT FALSE; - DECLARE veID INT; - DECLARE vExistingErrorID INT; - - DECLARE cur CURSOR FOR - SELECT cm.CoreMeasurementID - FROM coremeasurements cm - JOIN cmattributes cma ON cm.CoreMeasurementID = cma.CoreMeasurementID - JOIN attributes a ON cma.Code = a.Code - JOIN stems st ON cm.StemID = st.StemID - JOIN quadrats q ON st.QuadratID = q.QuadratID - WHERE ((cm.MeasuredDBH IS NOT NULL AND cm.MeasuredDBH > 0) OR - (cm.MeasuredHOM IS NOT NULL AND cm.MeasuredHOM > 0)) - AND a.Status IN ('dead', 'stem dead', 'missing', 'broken below', 'omitted') - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; - - CREATE TEMPORARY TABLE IF NOT EXISTS FailedValidations - ( - CoreMeasurementID INT - ); - - SELECT COUNT(*) - INTO expectedCount - FROM coremeasurements cm - JOIN cmattributes cma ON cm.CoreMeasurementID = cma.CoreMeasurementID - JOIN attributes a ON cma.Code = a.Code - JOIN stems st ON cm.StemID = st.StemID - JOIN quadrats q ON st.QuadratID = q.QuadratID - WHERE ((cm.MeasuredDBH IS NOT NULL AND cm.MeasuredDBH > 0) OR - (cm.MeasuredHOM IS NOT NULL AND cm.MeasuredHOM > 0)) - AND a.Status IN ('dead', 'stem dead', 'missing', 'broken below', 'omitted') - AND cm.IsValidated IS FALSE - AND (p_CensusID IS NULL OR q.CensusID = p_CensusID) - AND (p_PlotID IS NULL OR q.PlotID = p_PlotID); - - SELECT ValidationID - INTO veID - FROM catalog.validationprocedures - WHERE ProcedureName = 'ValidateScreenStemsWithMeasurementsButDeadAttributes'; - - - OPEN cur; - loop1: - LOOP - FETCH cur INTO vCoreMeasurementID; - IF done THEN - LEAVE loop1; - END IF; - - SET validationCriteria = 'Stem Measurements with Dead Attributes Validation'; - SET additionalDetails = 'Verifies that stems marked as dead do not have active measurements.'; - - IF EXISTS (SELECT 1 - FROM cmattributes cma - JOIN attributes a ON cma.Code = a.Code - JOIN coremeasurements cm on cma.CoreMeasurementID = cm.CoreMeasurementID - WHERE cma.CoreMeasurementID = vCoreMeasurementID - AND a.Status IN ('dead', 'stem dead', 'missing', 'broken below', 'omitted') - AND ((cm.MeasuredDBH IS NOT NULL AND cm.MeasuredDBH > 0) OR - (cm.MeasuredHOM IS NOT NULL AND cm.MeasuredHOM > 0))) THEN - SET validationResult = 0; - SET errorMessage = 'Stem with measurements but dead attributes detected.'; - -- Check if the error record already exists before inserting - IF NOT EXISTS (SELECT 1 - FROM cmverrors - WHERE CoreMeasurementID = vCoreMeasurementID - AND ValidationErrorID = veID) THEN - INSERT INTO cmverrors (CoreMeasurementID, ValidationErrorID) - VALUES (vCoreMeasurementID, veID); - END IF; - INSERT INTO FailedValidations (CoreMeasurementID) VALUES (vCoreMeasurementID); - SET insertCount = insertCount + 1; - ELSE - SET validationResult = 1; - SET errorMessage = NULL; - END IF; - - INSERT INTO validationchangelog (ProcedureName, RunDateTime, TargetRowID, - ValidationOutcome, ErrorMessage, - ValidationCriteria, AdditionalDetails) - VALUES ('ValidateScreenStemsWithMeasurementsButDeadAttributes', NOW(), vCoreMeasurementID, - IF(validationResult, 'Passed', 'Failed'), errorMessage, - validationCriteria, additionalDetails); - END LOOP; - CLOSE cur; - - SET successMessage = - CONCAT('Validation completed successfully. Total rows: ', expectedCount, ', Failed rows: ', insertCount); - SELECT expectedCount AS TotalRows, insertCount AS FailedRows, successMessage AS Message; - - SELECT CoreMeasurementID FROM FailedValidations; - - DROP TEMPORARY TABLE IF EXISTS FailedValidations; -END; -