diff --git a/.github/workflows/.deployer.yml b/.github/workflows/.deployer.yml index eabd6c8c5..1f44b3860 100644 --- a/.github/workflows/.deployer.yml +++ b/.github/workflows/.deployer.yml @@ -146,6 +146,7 @@ jobs: --set frontend.env.VITE_SSO_CLIENT_ID=${{ secrets.VITE_SSO_CLIENT_ID }} \ --set frontend.env.VITE_SSO_REALM=${{ secrets.VITE_SSO_REALM }} \ --set frontend.env.VITE_SSO_REDIRECT_URI=${{ secrets.VITE_SSO_REDIRECT_URI }} \ + --set frontend.env.VITE_API_URL=${{ secrets.VITE_API_URL }} \ ${{ inputs.params }} \ --install --wait --atomic ${{ steps.vars.outputs.release }} \ --timeout ${{ inputs.timeout-minutes }}m \ diff --git a/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/api/v1/messaging/NullMessageLog.java b/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/api/v1/messaging/NullMessageLog.java index 64f17b691..77290b09d 100644 --- a/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/api/v1/messaging/NullMessageLog.java +++ b/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/api/v1/messaging/NullMessageLog.java @@ -2,28 +2,18 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.text.MessageFormat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.slf4j.event.Level; public class NullMessageLog implements IMessageLog { - private static final Logger logger = LoggerFactory.getLogger(NullMessageLog.class); - - private final Level loggerLevel; - public NullMessageLog(Level loggerLevel) { - this.loggerLevel = loggerLevel; + /* do nothing */ } @Override public void addMessage(String message, Object... arguments) { - if (arguments.length > 0) { - message = MessageFormat.format(message, arguments); - } - logger.atLevel(loggerLevel).log(loggerLevel + " message: " + message); + /* do nothing */ } @Override diff --git a/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/projection/StubProjectionRunner.java b/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/projection/StubProjectionRunner.java index db6f932f2..71d0b52ab 100644 --- a/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/projection/StubProjectionRunner.java +++ b/backend/src/main/java/ca/bc/gov/nrs/vdyp/backend/projection/StubProjectionRunner.java @@ -1,15 +1,14 @@ package ca.bc.gov.nrs.vdyp.backend.projection; -import java.io.IOException; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Map; -import ca.bc.gov.nrs.vdyp.backend.api.v1.exceptions.ProjectionExecutionException; -import ca.bc.gov.nrs.vdyp.backend.api.v1.exceptions.ProjectionRequestValidationException; -import ca.bc.gov.nrs.vdyp.backend.model.v1.Parameters; -import ca.bc.gov.nrs.vdyp.backend.model.v1.ProjectionRequestKind; -import ca.bc.gov.nrs.vdyp.backend.projection.input.AbstractPolygonStream; -import ca.bc.gov.nrs.vdyp.backend.utils.FileHelper; +import ca.bc.gov.nrs.vdyp.backend.v1.api.impl.exceptions.ProjectionExecutionException; +import ca.bc.gov.nrs.vdyp.backend.v1.api.impl.exceptions.ProjectionRequestValidationException; +import ca.bc.gov.nrs.vdyp.backend.v1.gen.model.Parameters; +import ca.bc.gov.nrs.vdyp.backend.v1.gen.model.ProjectionRequestKind; +import jakarta.validation.Valid; public class StubProjectionRunner implements IProjectionRunner { @@ -39,28 +38,17 @@ public ProjectionState getState() { @Override public InputStream getYieldTable() throws ProjectionExecutionException { - try { - return FileHelper.getStubResourceFile("Output_YldTbl.csv"); - } catch (IOException e) { - throw new ProjectionExecutionException(e); - } + // No projection was done; therefore, there's no yield table. + return new ByteArrayInputStream(new byte[0]); } @Override - public InputStream getProgressStream() throws ProjectionExecutionException { - try { - return FileHelper.getStubResourceFile("Output_Log.txt"); - } catch (IOException e) { - throw new ProjectionExecutionException(e); - } + public InputStream getProgressStream() { + return state.getProgressLog().getAsStream(); } @Override - public InputStream getErrorStream() throws ProjectionExecutionException { - try { - return FileHelper.getStubResourceFile("Output_Error.txt"); - } catch (IOException e) { - throw new ProjectionExecutionException(e); - } + public InputStream getErrorStream() { + return state.getErrorLog().getAsStream(); } } diff --git a/backend/src/test/java/ca/bc/gov/nrs/vdyp/backend/endpoints/v1/StubHcsvProjectionEndpointTest.java b/backend/src/test/java/ca/bc/gov/nrs/vdyp/backend/endpoints/v1/StubHcsvProjectionEndpointTest.java new file mode 100644 index 000000000..b042c2bd5 --- /dev/null +++ b/backend/src/test/java/ca/bc/gov/nrs/vdyp/backend/endpoints/v1/StubHcsvProjectionEndpointTest.java @@ -0,0 +1,139 @@ +package ca.bc.gov.nrs.api.v1.endpoints; + +import static io.restassured.RestAssured.given; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.api.helpers.TestHelper; +import ca.bc.gov.nrs.vdyp.backend.v1.gen.api.ParameterNames; +import ca.bc.gov.nrs.vdyp.backend.v1.gen.model.Parameters; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.MediaType; + +@QuarkusTest +class StubHcsvProjectionEndpointTest { + + private final TestHelper testHelper; + + @Inject + StubHcsvProjectionEndpointTest(TestHelper testHelper) { + this.testHelper = testHelper; + } + + @BeforeEach + void setup() { + } + + @Test + void testProjectionHscv_shouldReturnStatusOK() throws IOException { + + Path resourceFolderPath = Path.of("VDYP7Console-sample-files", "hcsv", "vdyp-240"); + + Parameters parameters = testHelper.addSelectedOptions( + new Parameters(), // + Parameters.SelectedExecutionOptionsEnum.DO_ENABLE_DEBUG_LOGGING, + Parameters.SelectedExecutionOptionsEnum.DO_ENABLE_PROGRESS_LOGGING, + Parameters.SelectedExecutionOptionsEnum.DO_ENABLE_ERROR_LOGGING + ); + + // Included to generate JSON text of parameters as needed +// ObjectMapper mapper = new ObjectMapper(); +// String serializedParametersText = mapper.writeValueAsString(parameters); + + InputStream zipInputStream = given().basePath(TestHelper.ROOT_PATH).when() // + .multiPart(ParameterNames.PROJECTION_PARAMETERS, parameters, MediaType.APPLICATION_JSON) // + .multiPart( + ParameterNames.POLYGON_INPUT_DATA, + Files.readAllBytes(testHelper.getResourceFile(resourceFolderPath, "VDYP7_INPUT_POLY.csv")) + ) // + .multiPart( + ParameterNames.LAYERS_INPUT_DATA, + Files.readAllBytes(testHelper.getResourceFile(resourceFolderPath, "VDYP7_INPUT_LAYER.csv")) + ) // + .post("/projection/hcsv?trialRun=true") // + .then().statusCode(201) // + .and().contentType("application/octet-stream") // + .and().header("content-disposition", Matchers.startsWith("attachment;filename=\"vdyp-output-")) // + .extract().body().asInputStream(); + + ZipInputStream zipFile = new ZipInputStream(zipInputStream); + ZipEntry entry1 = zipFile.getNextEntry(); + assertEquals("YieldTable.csv", entry1.getName()); + String entry1Content = new String(testHelper.readZipEntry(zipFile, entry1)); + assertTrue(entry1Content.length() == 0); + + ZipEntry entry2 = zipFile.getNextEntry(); + assertEquals("ProgressLog.txt", entry2.getName()); + String entry2Content = new String(testHelper.readZipEntry(zipFile, entry2)); + assertTrue(entry2Content.startsWith("Running Projection")); + + ZipEntry entry3 = zipFile.getNextEntry(); + assertEquals("ErrorLog.txt", entry3.getName()); + String entry3Content = new String(testHelper.readZipEntry(zipFile, entry3)); + assertTrue(entry3Content.isBlank()); + + ZipEntry entry4 = zipFile.getNextEntry(); + assertEquals("DebugLog.txt", entry4.getName()); + String entry4Content = new String(testHelper.readZipEntry(zipFile, entry4)); + assertTrue(entry4Content.startsWith(LocalDate.now().format(DateTimeFormatter.ISO_DATE))); + } + + @Test + void testProjectionHscv_testNoProgressLogging() throws IOException { + + Path resourceFolderPath = Path.of("VDYP7Console-sample-files", "hcsv", "vdyp-240"); + + Parameters parameters = new Parameters(); + + InputStream zipInputStream = given().basePath(TestHelper.ROOT_PATH).when() // + .multiPart(ParameterNames.PROJECTION_PARAMETERS, parameters, MediaType.APPLICATION_JSON) // + .multiPart( + ParameterNames.POLYGON_INPUT_DATA, + Files.readAllBytes(testHelper.getResourceFile(resourceFolderPath, "VDYP7_INPUT_POLY.csv")) + ) // + .multiPart( + ParameterNames.LAYERS_INPUT_DATA, + Files.readAllBytes(testHelper.getResourceFile(resourceFolderPath, "VDYP7_INPUT_LAYER.csv")) + ) // + .post("/projection/hcsv?trialRun=true") // + .then().statusCode(201) // + .and().contentType("application/octet-stream") // + .and().header("content-disposition", Matchers.startsWith("attachment;filename=\"vdyp-output-")) // + .extract().body().asInputStream(); + + ZipInputStream zipFile = new ZipInputStream(zipInputStream); + ZipEntry entry1 = zipFile.getNextEntry(); + assertEquals("YieldTable.csv", entry1.getName()); + String entry1Content = new String(testHelper.readZipEntry(zipFile, entry1)); + assertTrue(entry1Content.length() == 0); + + ZipEntry entry2 = zipFile.getNextEntry(); + assertEquals("ProgressLog.txt", entry2.getName()); + String entry2Content = new String(testHelper.readZipEntry(zipFile, entry2)); + assertTrue(entry2Content.isBlank()); + + ZipEntry entry3 = zipFile.getNextEntry(); + assertEquals("ErrorLog.txt", entry3.getName()); + String entry3Content = new String(testHelper.readZipEntry(zipFile, entry3)); + assertTrue(entry3Content.isBlank()); + + ZipEntry entry4 = zipFile.getNextEntry(); + assertEquals("DebugLog.txt", entry4.getName()); + String entry4Content = new String(testHelper.readZipEntry(zipFile, entry4)); + assertTrue(entry4Content.isBlank()); + } +} diff --git a/charts/app/templates/frontend/templates/deployment.yaml b/charts/app/templates/frontend/templates/deployment.yaml index 4ceeecf9b..56134a956 100644 --- a/charts/app/templates/frontend/templates/deployment.yaml +++ b/charts/app/templates/frontend/templates/deployment.yaml @@ -47,7 +47,9 @@ spec: - name: VITE_SSO_REALM value: {{ .Values.frontend.env.VITE_SSO_REALM | quote }} - name: VITE_SSO_REDIRECT_URI - value: {{ .Values.frontend.env.VITE_SSO_REDIRECT_URI | quote }} + value: {{ .Values.frontend.env.VITE_SSO_REDIRECT_URI | quote }} + - name: VITE_API_URL + value: {{ .Values.frontend.env.VITE_API_URL | quote }} ports: - name: http containerPort: 3000 diff --git a/charts/app/values-dev.yaml b/charts/app/values-dev.yaml index a25c25e9f..1bc5d1e94 100644 --- a/charts/app/values-dev.yaml +++ b/charts/app/values-dev.yaml @@ -111,11 +111,12 @@ frontend: pdb: enabled: false # enable it in PRODUCTION for having pod disruption budget. minAvailable: 1 # the minimum number of pods that must be available during the disruption budget. - env: + env: VITE_SSO_AUTH_SERVER_URL: ~ VITE_SSO_CLIENT_ID: ~ VITE_SSO_REALM: ~ VITE_SSO_REDIRECT_URI: ~ + VITE_API_URL: ~ crunchy: # enable it for TEST and PROD, for PR based pipelines simply use single postgres enabled: false diff --git a/frontend/Caddyfile b/frontend/Caddyfile index a73702f89..37222e810 100644 --- a/frontend/Caddyfile +++ b/frontend/Caddyfile @@ -18,7 +18,7 @@ header { Content-Type text/javascript } - respond `window.config={"VITE_SSO_AUTH_SERVER_URL":"{$VITE_SSO_AUTH_SERVER_URL}", "VITE_SSO_CLIENT_ID":"{$VITE_SSO_CLIENT_ID}", "VITE_SSO_REALM":"{$VITE_SSO_REALM}", "VITE_SSO_REDIRECT_URI":"{$VITE_SSO_REDIRECT_URI}"};` + respond `window.config={"VITE_SSO_AUTH_SERVER_URL":"{$VITE_SSO_AUTH_SERVER_URL}", "VITE_SSO_CLIENT_ID":"{$VITE_SSO_CLIENT_ID}", "VITE_SSO_REALM":"{$VITE_SSO_REALM}", "VITE_SSO_REDIRECT_URI":"{$VITE_SSO_REDIRECT_URI}", "VITE_API_URL":"{$VITE_API_URL}"};` } root * /srv encode zstd gzip @@ -31,10 +31,12 @@ } rewrite @spa_router {http.matchers.file.relative} # Proxy requests to API service - reverse_proxy /api/* {$BACKEND_URL} { - header_up Host {http.reverse_proxy.upstream.hostport} - header_up X-Real-IP {remote_host} - header_up X-Forwarded-For {remote_host} + handle_path /api/* { + reverse_proxy {$VITE_API_URL} { + header_up Host {http.reverse_proxy.upstream.hostport} + header_up X-Real-IP {remote_host} + header_up X-Forwarded-For {remote_host} + } } header { X-Frame-Options "SAMEORIGIN" @@ -42,7 +44,7 @@ Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" X-Content-Type-Options "nosniff" Strict-Transport-Security "max-age=31536000" - Content-Security-Policy "default-src 'self' https://spt.apps.gov.bc.ca data:; script-src 'self' 'unsafe-eval' https://www2.gov.bc.ca; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://use.fontawesome.com https://cdn.jsdelivr.net; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net; img-src 'self' data: https://fonts.googleapis.com http://www.w3.org https://*.gov.bc.ca; frame-src 'self' https://dev.loginproxy.gov.bc.ca https://login.microsoftonline.com; connect-src 'self' https://dev.loginproxy.gov.bc.ca" + Content-Security-Policy "default-src 'self' https://spt.apps.gov.bc.ca data:; script-src 'self' 'unsafe-eval' https://www2.gov.bc.ca; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://use.fontawesome.com https://cdn.jsdelivr.net; font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net; img-src 'self' data: https://fonts.googleapis.com http://www.w3.org https://*.gov.bc.ca; frame-src 'self' https://*.gov.bc.ca https://login.microsoftonline.com; connect-src 'self' https://*.gov.bc.ca" Referrer-Policy "same-origin" Feature-Policy "fullscreen 'self'; camera 'none'; microphone 'none'" } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b032f5990..fa6027b9b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,6 +18,7 @@ "axios": "1.7.7", "file-saver": "2.0.5", "http-status-codes": "2.3.0", + "jszip": "3.10.1", "keycloak-js": "25.0.2", "papaparse": "5.4.1", "pinia": "2.2.2", @@ -26,7 +27,6 @@ "vite-plugin-package-version": "1.1.0", "vue": "3.4.29", "vue-router": "4.3.3", - "vue-slider-component": "4.1.0-beta.7", "vuetify": "3.6.14" }, "devDependencies": { @@ -1583,10 +1583,15 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -2285,6 +2290,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/immutable": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", @@ -2330,8 +2340,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -2384,6 +2393,11 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2434,6 +2448,17 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/jwt-decode": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", @@ -2473,6 +2498,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2738,6 +2771,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/papaparse": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", @@ -2958,6 +2996,11 @@ "resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz", "integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==" }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3005,6 +3048,20 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3111,6 +3168,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/sass": { "version": "1.77.6", "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", @@ -3180,6 +3242,11 @@ "node": ">=10" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3227,6 +3294,14 @@ "node": ">=0.10.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3380,9 +3455,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "peer": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/vdyp": { "resolved": "", @@ -3537,11 +3610,6 @@ "vue": "^3.2.0" } }, - "node_modules/vue-slider-component": { - "version": "4.1.0-beta.7", - "resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-4.1.0-beta.7.tgz", - "integrity": "sha512-Qb7K920ZG7PoQswoF6Ias+i3W2rd3k4fpk04JUl82kEUcN86Yg6et7bVSKWt/7VpQe8a5IT3BqCKSCOZ7AJgCA==" - }, "node_modules/vue-tsc": { "version": "2.1.10", "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz", diff --git a/frontend/package.json b/frontend/package.json index d1897f2f8..6b38ea6a4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,6 +27,7 @@ "axios": "1.7.7", "file-saver": "2.0.5", "http-status-codes": "2.3.0", + "jszip": "3.10.1", "keycloak-js": "25.0.2", "papaparse": "5.4.1", "pinia": "2.2.2", @@ -35,7 +36,6 @@ "vite-plugin-package-version": "1.1.0", "vue": "3.4.29", "vue-router": "4.3.3", - "vue-slider-component": "4.1.0-beta.7", "vuetify": "3.6.14" }, "devDependencies": { diff --git a/frontend/src/components/JobTypeSelection.vue b/frontend/src/components/JobTypeSelection.vue index 462f57bcd..e314c0eb6 100644 --- a/frontend/src/components/JobTypeSelection.vue +++ b/frontend/src/components/JobTypeSelection.vue @@ -4,8 +4,8 @@ Model - - Version - - - - Job ID - - diff --git a/frontend/src/components/model-param-selection-panes/ReportInfo.vue b/frontend/src/components/model-param-selection-panes/ReportInfo.vue index 59024c347..c5172e19a 100644 --- a/frontend/src/components/model-param-selection-panes/ReportInfo.vue +++ b/frontend/src/components/model-param-selection-panes/ReportInfo.vue @@ -138,7 +138,7 @@ Report Title - + -
- - - - - - - - - -
{ projectionType.value = DEFAULT_VALUES.PROJECTION_TYPE speciesGroups.value = speciesGroups.value.map((group) => ({ ...group, - minimumDBHLimit: MINIMUM_DBH_LIMITS.CM4_0, })) } diff --git a/frontend/src/components/model-param-selection-panes/SiteInfo.vue b/frontend/src/components/model-param-selection-panes/SiteInfo.vue index 34341021a..f3f0c035d 100644 --- a/frontend/src/components/model-param-selection-panes/SiteInfo.vue +++ b/frontend/src/components/model-param-selection-panes/SiteInfo.vue @@ -21,7 +21,7 @@
- +
- + - - - - *Ministry Default Curve for this Species - - @@ -142,120 +123,6 @@ - - - - - - {{ MDL_PRM_INPUT_HINT.SITE_ZERO_NOT_KNOW }} - - - - - - {{ MDL_PRM_INPUT_HINT.SITE_DFT_COMPUTED }} - - - -
- - -
-
- {{ SPIN_BUTTON.UP }} -
-
- {{ SPIN_BUTTON.DOWN }} -
-
-
-
- - {{ MDL_PRM_INPUT_HINT.SITE_ZERO_NOT_KNOW }} - -
-
- - - -
- -
- - - -
-
@@ -365,16 +214,10 @@ import { becZoneOptions, ecoZoneOptions, siteSpeciesValuesOptions, - ageTypeOptions, - floatingOptions, } from '@/constants/options' -import { SITE_INDEX_CURVE_MAP } from '@/constants/mappings' import { PANEL, DERIVED_BY, - SITE_SPECIES_VALUES, - FLOATING, - SPECIAL_INDICATORS, MODEL_PARAMETER_PANEL, NUM_INPUT_LIMITS, CONTINUOUS_INC_DEC, @@ -404,14 +247,8 @@ const { becZone, ecoZone, incSecondaryHeight, - siteIndexCurve, siteSpeciesValues, - ageType, - percentStockableArea, - age, - height, bha50SiteIndex, - floating, } = storeToRefs(modelParameterStore) const panelName = MODEL_PARAMETER_PANEL.SITE_INFO @@ -422,17 +259,6 @@ const isConfirmed = computed( () => modelParameterStore.panelState[panelName].confirmed, ) -const computedSpeciesOptions = computed(() => - ( - Object.keys(SITE_INDEX_CURVE_MAP) as Array< - keyof typeof SITE_INDEX_CURVE_MAP - > - ).map((code) => ({ - label: `${SITE_INDEX_CURVE_MAP[code]}`, - value: code, - })), -) - const siteSpeciesOptions = computed(() => speciesGroups.value.map((group) => ({ label: group.siteSpecies, @@ -443,139 +269,36 @@ const siteSpeciesOptions = computed(() => const isIncSecondaryHeightDisabled = ref(false) const isSelectedSiteSpeciesDisabled = ref(false) const isSiteSpeciesValueDisabled = ref(false) -const isAgeTypeDisabled = ref(false) -const isAgeDisabled = ref(false) -const isHeightDisabled = ref(false) const isBHA50SiteIndexDisabled = ref(false) -const isFloatingDisabled = ref(false) - -const agePlaceholder = ref('') -const heightPlaceholder = ref('') // Interval references for continuous increment/decrement -let heightIncrementInterval: number | null = null -let heightDecrementInterval: number | null = null let bha50IncrementInterval: number | null = null let bha50DecrementInterval: number | null = null -const setFloatingState = (newFloating: string | null) => { - isAgeTypeDisabled.value = false - isAgeDisabled.value = false - isHeightDisabled.value = false - isBHA50SiteIndexDisabled.value = false - floating.value = newFloating - - if (newFloating === FLOATING.AGE) { - isAgeTypeDisabled.value = true - isAgeDisabled.value = true - } else if (newFloating === FLOATING.HEIGHT) { - isHeightDisabled.value = true - } else if (newFloating === FLOATING.SITEINDEX) { - isBHA50SiteIndexDisabled.value = true - } -} - -const handleSiteSpeciesValuesState = ( - newSiteSpeciesValues: string | null, - newFloating: string | null, -) => { - if (newSiteSpeciesValues === SITE_SPECIES_VALUES.COMPUTED) { - isFloatingDisabled.value = false - setFloatingState(newFloating) - - // TODO - set values based on species, beczone, agetype - // if age or height float is selected, age, height or bha 50 site index should also be factored into the calculation for these values - age.value = 60 - height.value = DEFAULT_VALUES.HEIGHT - - agePlaceholder.value = '' - heightPlaceholder.value = '' - } else if (newSiteSpeciesValues === SITE_SPECIES_VALUES.SUPPLIED) { - isAgeTypeDisabled.value = true - isAgeDisabled.value = true - isHeightDisabled.value = true - isBHA50SiteIndexDisabled.value = false - isFloatingDisabled.value = true - - age.value = null - height.value = null - agePlaceholder.value = SPECIAL_INDICATORS.NA - heightPlaceholder.value = SPECIAL_INDICATORS.NA - } -} - const handleDerivedByChange = ( newDerivedBy: string | null, newSiteSpecies: string | null, - newSiteSpeciesValues: string | null, - newFloating: string | null, ) => { if (newDerivedBy === DERIVED_BY.VOLUME) { incSecondaryHeight.value = false isIncSecondaryHeightDisabled.value = true isSelectedSiteSpeciesDisabled.value = true - handleSiteSpeciesValuesState(newSiteSpeciesValues, newFloating) } else if (newDerivedBy === DERIVED_BY.BASAL_AREA) { isIncSecondaryHeightDisabled.value = false isSelectedSiteSpeciesDisabled.value = false isSiteSpeciesValueDisabled.value = newSiteSpecies !== highestPercentSpecies.value - handleSiteSpeciesValuesState(newSiteSpeciesValues, newFloating) - } -} - -// Update siteIndexCurve based on selectedSiteSpecies -const updateSiteIndexCurve = (newSiteSpecies: string | null) => { - if ( - newSiteSpecies && - SITE_INDEX_CURVE_MAP[newSiteSpecies as keyof typeof SITE_INDEX_CURVE_MAP] - ) { - siteIndexCurve.value = - SITE_INDEX_CURVE_MAP[newSiteSpecies as keyof typeof SITE_INDEX_CURVE_MAP] - } else { - siteIndexCurve.value = null // Clear if no mapping found } } -watch(selectedSiteSpecies, (newSiteSpecies) => { - updateSiteIndexCurve(newSiteSpecies) -}) - watch( - [derivedBy, selectedSiteSpecies, siteSpeciesValues, floating], - ([newDerivedBy, newSiteSpecies, newSiteSpeciesValues, newFloating]) => { - handleDerivedByChange( - newDerivedBy, - newSiteSpecies, - newSiteSpeciesValues, - newFloating, - ) + [derivedBy, selectedSiteSpecies, siteSpeciesValues], + ([newDerivedBy, newSiteSpecies]) => { + handleDerivedByChange(newDerivedBy, newSiteSpecies) }, { immediate: true }, ) -const incrementHeight = () => { - const newValue = Util.increaseItemBySpinButton( - height.value, - NUM_INPUT_LIMITS.HEIGHT_MAX, - NUM_INPUT_LIMITS.HEIGHT_MIN, - NUM_INPUT_LIMITS.HEIGHT_STEP, - ) - - height.value = newValue.toFixed(NUM_INPUT_LIMITS.HEIGHT_DECIMAL_NUM) -} - -const decrementHeight = () => { - let newValue = Util.decrementItemBySpinButton( - height.value, - NUM_INPUT_LIMITS.HEIGHT_MAX, - NUM_INPUT_LIMITS.HEIGHT_MIN, - NUM_INPUT_LIMITS.HEIGHT_STEP, - ) - - height.value = newValue.toFixed(NUM_INPUT_LIMITS.HEIGHT_DECIMAL_NUM) -} - const incrementBHA50SiteIndex = () => { const newValue = Util.increaseItemBySpinButton( bha50SiteIndex.value, @@ -602,37 +325,6 @@ const decrementBHA50SiteIndex = () => { ) } -// Methods to handle continuous increment/decrement for Height -const startIncrementHeight = () => { - incrementHeight() - heightIncrementInterval = window.setInterval( - incrementHeight, - CONTINUOUS_INC_DEC.INTERVAL, - ) -} - -const stopIncrementHeight = () => { - if (heightIncrementInterval !== null) { - clearInterval(heightIncrementInterval) - heightIncrementInterval = null - } -} - -const startDecrementHeight = () => { - decrementHeight() - heightDecrementInterval = window.setInterval( - decrementHeight, - CONTINUOUS_INC_DEC.INTERVAL, - ) -} - -const stopDecrementHeight = () => { - if (heightDecrementInterval !== null) { - clearInterval(heightDecrementInterval) - heightDecrementInterval = null - } -} - // Methods to handle continuous increment/decrement for BHA 50 Site Index const startIncrementBHA50SiteIndex = () => { incrementBHA50SiteIndex() @@ -665,37 +357,6 @@ const stopDecrementBHA50SiteIndex = () => { } const validateRange = (): boolean => { - if ( - !siteInfoValidator.validatePercentStockableAreaRange( - percentStockableArea.value, - ) - ) { - messageDialogStore.openDialog( - MSG_DIALOG_TITLE.INVALID_INPUT, - MDL_PRM_INPUT_ERR.SITE_VLD_PCT_STCB_AREA_RNG, - { width: 400 }, - ) - return false - } - - if (!siteInfoValidator.validateAgeRange(age.value)) { - messageDialogStore.openDialog( - MSG_DIALOG_TITLE.INVALID_INPUT, - MDL_PRM_INPUT_ERR.SITE_VLD_AGE_RNG, - { width: 400 }, - ) - return false - } - - if (!siteInfoValidator.validateHeightRange(height.value)) { - messageDialogStore.openDialog( - MSG_DIALOG_TITLE.INVALID_INPUT, - MDL_PRM_INPUT_ERR.SITE_VLD_HIGHT_RNG, - { width: 400 }, - ) - return false - } - if (!siteInfoValidator.validateBha50SiteIndexRange(bha50SiteIndex.value)) { messageDialogStore.openDialog( MSG_DIALOG_TITLE.INVALID_INPUT, @@ -709,21 +370,10 @@ const validateRange = (): boolean => { } const validateRequiredFields = (): boolean => { - if ( - !siteInfoValidator.validateRequiredFields( - siteSpeciesValues.value, - age.value, - height.value, - bha50SiteIndex.value, - ) - ) { + if (!siteInfoValidator.validateRequiredFields(bha50SiteIndex.value)) { messageDialogStore.openDialog( MSG_DIALOG_TITLE.MISSING_INFO, - siteSpeciesValues.value === SITE_SPECIES_VALUES.COMPUTED - ? MDL_PRM_INPUT_ERR.SITE_VLD_SPCZ_REQ_VALS_SUP( - selectedSiteSpecies.value, - ) - : MDL_PRM_INPUT_ERR.SITE_VLD_SPCZ_REQ_SI_VAL(selectedSiteSpecies.value), + MDL_PRM_INPUT_ERR.SITE_VLD_SPCZ_REQ_SI_VAL(selectedSiteSpecies.value), { width: 400 }, ) return false @@ -733,12 +383,6 @@ const validateRequiredFields = (): boolean => { } const formattingValues = (): void => { - if (height.value) { - height.value = parseFloat(height.value).toFixed( - NUM_INPUT_LIMITS.HEIGHT_DECIMAL_NUM, - ) - } - if (bha50SiteIndex.value) { bha50SiteIndex.value = parseFloat(bha50SiteIndex.value).toFixed( NUM_INPUT_LIMITS.BHA50_SITE_INDEX_DECIMAL_NUM, @@ -774,19 +418,11 @@ const clear = () => { } selectedSiteSpecies.value = highestPercentSpecies.value - updateSiteIndexCurve(selectedSiteSpecies.value) becZone.value = DEFAULT_VALUES.BEC_ZONE siteSpeciesValues.value = DEFAULT_VALUES.SITE_SPECIES_VALUES - ageType.value = DEFAULT_VALUES.AGE_TYPE - floating.value = DEFAULT_VALUES.FLOATING - - handleDerivedByChange( - derivedBy.value, - selectedSiteSpecies.value, - siteSpeciesValues.value, - floating.value, - ) + + handleDerivedByChange(derivedBy.value, selectedSiteSpecies.value) } diff --git a/frontend/src/components/model-param-selection-panes/StandDensity.vue b/frontend/src/components/model-param-selection-panes/StandDensity.vue index 12031d690..f385b69fd 100644 --- a/frontend/src/components/model-param-selection-panes/StandDensity.vue +++ b/frontend/src/components/model-param-selection-panes/StandDensity.vue @@ -19,177 +19,29 @@ -
- - -
- - - - -
-
- {{ SPIN_BUTTON.UP }} -
-
- {{ SPIN_BUTTON.DOWN }} -
-
-
-
-
- - - {{ - MDL_PRM_INPUT_HINT.DENSITY_WO_AGE - }} - -
- - -
- - - -
-
- {{ SPIN_BUTTON.UP }} -
-
- {{ SPIN_BUTTON.DOWN }} -
-
-
-
-
- - - - -
- - - - {{ MDL_PRM_INPUT_HINT.DENSITY_PCC_APPLY_DFT }} - - - + + + {{ MDL_PRM_INPUT_HINT.SITE_DFT_COMPUTED }} - - - -
+
+
diff --git a/frontend/src/constants/constants.ts b/frontend/src/constants/constants.ts index 07def59c3..09c1ee1d8 100644 --- a/frontend/src/constants/constants.ts +++ b/frontend/src/constants/constants.ts @@ -25,34 +25,9 @@ export const DERIVED_BY = Object.freeze({ }) export const SITE_SPECIES_VALUES = Object.freeze({ - COMPUTED: 'Computed', SUPPLIED: 'Supplied', }) -export const AGE_TYPE = Object.freeze({ - TOTAL: 'Total', - BREAST: 'Breast', -}) - -export const FLOATING = Object.freeze({ - AGE: 'Age', - HEIGHT: 'Height', - SITEINDEX: 'SiteIndex', -}) - -export const COMPUTED_VALUES = Object.freeze({ - USE: 'Use', - MODIFY: 'Modify', -}) - -export const MINIMUM_DBH_LIMITS = Object.freeze({ - CM4_0: '4.0 cm+', - CM7_5: '7.5 cm+', - CM12_5: '12.5 cm+', - CM17_5: '17.5 cm+', - CM22_5: '22.5 cm+', -}) - export const VOLUME_REPORTED = Object.freeze({ WHOLE_STEM: 'Whole Stem', CLOSE_UTIL: 'Close Utilization', @@ -75,7 +50,6 @@ export const MODEL_PARAMETER_PANEL = Object.freeze({ SPECIES_INFO: 'speciesInfo', SITE_INFO: 'siteInfo', STAND_DENSITY: 'standDensity', - ADDT_STAND_ATTRS: 'addtStandAttrs', REPORT_INFO: 'reportInfo', }) @@ -85,13 +59,6 @@ export const NUM_INPUT_LIMITS = Object.freeze({ SPECIES_PERCENT_STEP: 5, SPECIES_PERCENT_DECIMAL_NUM: 1, TOTAL_SPECIES_PERCENT: 100, - AGE_MAX: 500, - AGE_MIN: 0, - AGE_STEP: 10, - HEIGHT_MAX: 99.9, - HEIGHT_MIN: 0, - HEIGHT_STEP: 1, - HEIGHT_DECIMAL_NUM: 2, BHA50_SITE_INDEX_MAX: 60, BHA50_SITE_INDEX_MIN: 0, BHA50_SITE_INDEX_STEP: 1, @@ -99,38 +66,6 @@ export const NUM_INPUT_LIMITS = Object.freeze({ PERCENT_STOCKABLE_AREA_MAX: 100, PERCENT_STOCKABLE_AREA_MIN: 0, PERCENT_STOCKABLE_AREA_STEP: 5, - BASAL_AREA_MAX: 250, - BASAL_AREA_MIN: 0.1, - BASAL_AREA_STEP: 2.5, - BASAL_AREA_DECIMAL_NUM: 4, - TPH_MAX: 9999.9, - TPH_MIN: 0.1, - TPH_STEP: 250, - TPH_DECIMAL_NUM: 2, - CROWN_CLOSURE_MAX: 100, - CROWN_CLOSURE_MIN: 0, - CROWN_CLOSURE_STEP: 5, - LOREY_HEIGHT_MAX: 99.9, - LOREY_HEIGHT_MIN: 0.01, - LOREY_HEIGHT_DECIMAL_NUM: 2, - WHOLE_STEM_VOL75_MAX: 2500, - WHOLE_STEM_VOL75_MIN: 0.1, - WHOLE_STEM_VOL75_DECIMAL_NUM: 1, - BASAL_AREA125_MAX: 250, - BASAL_AREA125_MIN: 0.1, - BASAL_AREA125_DECIMAL_NUM: 4, - WHOLE_STEM_VOL125_MAX: 2500, - WHOLE_STEM_VOL125_MIN: 0, - WHOLE_STEM_VOL125_DECIMAL_NUM: 1, - CU_VOL_MAX: 2500, - CU_VOL_MIN: 0, - CU_VOL_DECIMAL_NUM: 1, - CU_NET_DECAY_VOL_MAX: 2500, - CU_NET_DECAY_VOL_MIN: 0, - CU_NET_DECAY_DECIMAL_NUM: 1, - CU_NET_DECAY_WASTE_VOL_MAX: 2500, - CU_NET_DECAY_WASTE_VOL_MIN: 0, - CU_NET_DECAY_WASTE_DECIMAL_NUM: 1, STARTING_AGE_MAX: 500, STARTING_AGE_MIN: 0, STARTING_AGE_STEP: 10, @@ -173,14 +108,8 @@ export const MODEL_SELECTION = Object.freeze({ INPUT_MODEL_PARAMETERS: 'Input Model Parameters', }) -export const ENGINE_VERSION = Object.freeze({ - VDYP8: 'VDYP 8', - VDYP9: 'VDYP 9', -}) - export const MODEL_PARAM_TAB_NAME = Object.freeze({ MODEL_PARAM_SELECTION: 'Model Parameter Selection', - FILE_UPLOAD: 'File Upload', MODEL_REPORT: 'Model Report', VIEW_LOG_FILE: 'View Log File', VIEW_ERROR_MESSAGES: 'View Error Messages', diff --git a/frontend/src/constants/defaults.ts b/frontend/src/constants/defaults.ts index 9d105c1d3..4b8932373 100644 --- a/frontend/src/constants/defaults.ts +++ b/frontend/src/constants/defaults.ts @@ -3,26 +3,9 @@ import * as CONSTANTS from '@/constants/constants' export const DEFAULT_VALUES = Object.freeze({ DERIVED_BY: CONSTANTS.DERIVED_BY.VOLUME, BEC_ZONE: 'IDF', - SITE_SPECIES_VALUES: CONSTANTS.SITE_SPECIES_VALUES.COMPUTED, - AGE_TYPE: CONSTANTS.AGE_TYPE.TOTAL, - PERCENT_STOCKABLE_AREA: 55, - AGE: 60, - HEIGHT: '17.00', + SITE_SPECIES_VALUES: CONSTANTS.SITE_SPECIES_VALUES.SUPPLIED, BHA50_SITE_INDEX: '16.30', - FLOATING: CONSTANTS.FLOATING.SITEINDEX, - BASAL_AREA: '10.0000', - TPH: '1000.00', - MINIMUM_DBH_LIMIT: CONSTANTS.MINIMUM_DBH_LIMITS.CM7_5, - CURRENT_DIAMETER: '11.3', - PERCENT_CROWN_CLOSURE: 0, - COMPUTED_VALUES: CONSTANTS.COMPUTED_VALUES.USE, - LOREY_HEIGHT: '14.47', - WHOLE_STEM_VOL75: '61.1', - BASAL_AREA125: '4.7545', - WHOLE_STEM_VOL125: '34.8', - CU_VOL: '23.6', - CU_NET_DECAY_VOL: '22.6', - CU_NET_DECAY_WASTE_VOL: '22.2', + PERCENT_STOCKABLE_AREA: 55, STARTING_AGE: 0, FINISHING_AGE: 250, AGE_INCREMENT: 25, diff --git a/frontend/src/constants/mappings.ts b/frontend/src/constants/mappings.ts index e9d37e63a..bc7daf50d 100644 --- a/frontend/src/constants/mappings.ts +++ b/frontend/src/constants/mappings.ts @@ -33,253 +33,3 @@ export const SPECIES_MAP = { SW: 'White Spruce', YC: 'Yellow Cedar', } - -// Mapping species code and default Site Index Curve name -export const SITE_INDEX_CURVE_MAP = { - AC: 'Huang, Titus, and Lakusta (1994ac)', - AT: 'Nigh, Krestov, and Klinka 2002', - B: 'Chen and Klinka (2000ac)', - BA: 'Nigh (2009)', - BG: 'Nigh (2009)', - BL: 'Chen and Klinka (2000ac)', - CW: 'Nigh (2000)', - DR: 'Nigh and Courtin (1998)', - E: 'Nigh (2009)', - EA: 'Nigh (2009)', - EP: 'Nigh (2009)', - FD: 'Thrower and Goudie (1992ac)', - H: 'Nigh (1998)', - HM: 'Means, Campbell, Johnson (1988ac)', - HW: 'Nigh (1998)', - L: 'Brisco, Klinka, Nigh 2002', - LA: 'Brisco, Klinka, Nigh 2002', - LT: 'Brisco, Klinka, Nigh 2002', - LW: 'Brisco, Klinka, Nigh 2002', - MB: 'Nigh and Courtin (1998)', - PA: 'Thrower (1994)', - PF: 'Thrower (1994)', - PJ: 'Huang (1997ac)', - PL: 'Thrower (1994)', - PW: 'Curtis, Diaz, and Clendenen (1990ac)', - PY: 'Nigh (2002)', - S: 'Goudie (1984ac) (natural)', - SB: 'Nigh, Krestov, and Klinka 2002', - SE: 'Nigh (2015)', - SS: 'Nigh (1997)', - SW: 'Goudie (1984ac) (natural)', - YC: 'Nigh (2000)', -} - -// Mapping bec zone code and coastal or not -export const BEC_ZONE_COASTAL_MAP: Record = { - AT: false, - BG: false, - BWBS: false, - CDF: true, - CWH: true, - ESSF: false, - ICH: false, - IDF: false, - MH: true, - MS: false, - PP: false, - SBPS: false, - SBS: false, - SWB: false, -} - -// BA_LIMIT_COEFFICIENTS stores the basal area limit coefficients for different species (AC, AT, B, etc.). -// Each species has separate coefficient values for coastal and interior regions, represented by 'coeff1' and 'coeff2'. -// These coefficients are used in an exponential equation to calculate the maximum allowable basal area based on species and location. -// A value of -999 indicates that no valid limit exists for the specific region and species combination. (from vdyp.ini) -export const BA_LIMIT_COEFFICIENTS = { - AC: { - coastal: { coeff1: 107.240519, coeff2: -14.377881 }, - interior: { coeff1: 118.629456, coeff2: -19.159803 }, - }, - AT: { - coastal: { coeff1: -999, coeff2: -999 }, - interior: { coeff1: 98.298267, coeff2: -15.823783 }, - }, - B: { - coastal: { coeff1: 134.265995, coeff2: -10.723979 }, - interior: { coeff1: 103.717551, coeff2: -12.032769 }, - }, - C: { - coastal: { coeff1: 199.94291, coeff2: -14.931348 }, - interior: { coeff1: 393.75934, coeff2: -35.40266 }, - }, - D: { - coastal: { coeff1: 107.240519, coeff2: -14.377881 }, - interior: { coeff1: -999, coeff2: -999 }, - }, - E: { - coastal: { coeff1: 107.240519, coeff2: -14.377881 }, - interior: { coeff1: 118.629456, coeff2: -19.159803 }, - }, - F: { - coastal: { coeff1: 213.706529, coeff2: -28.643038 }, - interior: { coeff1: 132.594246, coeff2: -20.216383 }, - }, - H: { - coastal: { coeff1: 144.825311, coeff2: -13.110869 }, - interior: { coeff1: 122.420409, coeff2: -10.923619 }, - }, - L: { - coastal: { coeff1: -999, coeff2: -999 }, - interior: { coeff1: 119.642742, coeff2: -21.246736 }, - }, - MB: { - coastal: { coeff1: 107.240519, coeff2: -14.377881 }, - interior: { coeff1: -999, coeff2: -999 }, - }, - PL: { - coastal: { coeff1: 185.048127, coeff2: -19.900699 }, - interior: { coeff1: 95.118542, coeff2: -12.154888 }, - }, - PW: { - coastal: { coeff1: -999, coeff2: -999 }, - interior: { coeff1: 158.465684, coeff2: -26.781112 }, - }, - PY: { - coastal: { coeff1: -999, coeff2: -999 }, - interior: { coeff1: 71.943238, coeff2: -14.264704 }, - }, - S: { - coastal: { coeff1: 177.814415, coeff2: -13.714547 }, - interior: { coeff1: 96.84127, coeff2: -12.60781 }, - }, -} - -// BA_EQUATION_CONSTANTS defines the constant values used in the basal area limit equation. -// These constants (const1, const2, and const3) are applied to calculate the final basal area limit based on species-specific coefficients. (from vdyp.ini) -export const BA_EQUATION_CONSTANTS = { - const1: 5, - const2: 1.3, - const3: -1, -} - -export const TPH_LIMIT_COEFFICIENTS = { - AC: { - coastal: { - P10: { a0: 7.5, b0: 0.184064, b1: 0.005592 }, - P90: { a0: 7.5, b0: 0.96373, b1: 0.00453 }, - }, - interior: { - P10: { a0: 7.5, b0: -0.084114, b1: 0.016436 }, - P90: { a0: 7.5, b0: 0.58714, b1: 0.022826 }, - }, - }, - AT: { - interior: { - P10: { a0: 7.5, b0: 0.00544, b1: 0.010618 }, - P90: { a0: 7.5, b0: 0.660157, b1: 0.011754 }, - }, - }, - B: { - coastal: { - P10: { a0: 7.5, b0: 0.229925, b1: 0.005735 }, - P90: { a0: 7.5, b0: 1.226133, b1: -0.002427 }, - }, - interior: { - P10: { a0: 7.5, b0: 0.184201, b1: 0.006065 }, - P90: { a0: 7.5, b0: 1.059981, b1: -0.000686 }, - }, - }, - C: { - coastal: { - P10: { a0: 7.5, b0: 0.387454, b1: 0.002709 }, - P90: { a0: 7.5, b0: 1.45061, b1: -0.000679 }, - }, - interior: { - P10: { a0: 7.5, b0: 0.103056, b1: 0.012318 }, - P90: { a0: 7.5, b0: 0.2699, b1: 0.042869 }, - }, - }, - D: { - coastal: { - P10: { a0: 7.5, b0: 0.184064, b1: 0.005592 }, - P90: { a0: 7.5, b0: 0.96373, b1: 0.00453 }, - }, - }, - E: { - coastal: { - P10: { a0: 7.5, b0: 0.184064, b1: 0.005592 }, - P90: { a0: 7.5, b0: 0.96373, b1: 0.00453 }, - }, - interior: { - P10: { a0: 7.5, b0: -0.084114, b1: 0.016436 }, - P90: { a0: 7.5, b0: 0.58714, b1: 0.022826 }, - }, - }, - F: { - coastal: { - P10: { a0: 7.5, b0: 0.116002, b1: 0.006594 }, - P90: { a0: 7.5, b0: 0.68269, b1: 0.008622 }, - }, - interior: { - P10: { a0: 7.5, b0: 0.123477, b1: 0.005786 }, - P90: { a0: 7.5, b0: 1.193114, b1: -0.006459 }, - }, - }, - H: { - coastal: { - P10: { a0: 7.5, b0: 0.126113, b1: 0.007561 }, - P90: { a0: 7.5, b0: 1.207655, b1: -0.001023 }, - }, - interior: { - P10: { a0: 7.5, b0: 0.014342, b1: 0.012198 }, - P90: { a0: 7.5, b0: 0.79931, b1: 0.013942 }, - }, - }, - L: { - interior: { - P10: { a0: 7.5, b0: 0.06893, b1: 0.005579 }, - P90: { a0: 7.5, b0: 0.31423, b1: 0.015952 }, - }, - }, - MB: { - coastal: { - P10: { a0: 7.5, b0: 0.184064, b1: 0.005592 }, - P90: { a0: 7.5, b0: 0.96373, b1: 0.00453 }, - }, - }, - PL: { - coastal: { - P10: { a0: 7.5, b0: -0.083294, b1: 0.014145 }, - P90: { a0: 7.5, b0: 0.938361, b1: -0.003504 }, - }, - interior: { - P10: { a0: 7.5, b0: -0.083294, b1: 0.014145 }, - P90: { a0: 7.5, b0: 0.938361, b1: -0.003504 }, - }, - }, - PW: { - interior: { - P10: { a0: 7.5, b0: 0.031801, b1: 0.007887 }, - P90: { a0: 7.5, b0: 0.909946, b1: -0.005477 }, - }, - }, - PY: { - interior: { - P10: { a0: 7.5, b0: 0.267422, b1: 0.009514 }, - P90: { a0: 7.5, b0: 1.922409, b1: -0.008496 }, - }, - }, - S: { - coastal: { - P10: { a0: 7.5, b0: 0.16879, b1: 0.008936 }, - P90: { a0: 7.5, b0: 0.8714, b1: 0.011812 }, - }, - interior: { - P10: { a0: 7.5, b0: 0.124051, b1: 0.007309 }, - P90: { a0: 7.5, b0: 0.910138, b1: 0.002576 }, - }, - }, -} - -export const TPH_EQUATION_CONSTANTS = { - const1: 5.0, - const2: 1.3, - const3: 0.00007854, -} diff --git a/frontend/src/constants/message.ts b/frontend/src/constants/message.ts index 48c4806ba..c736fd1c6 100644 --- a/frontend/src/constants/message.ts +++ b/frontend/src/constants/message.ts @@ -36,6 +36,11 @@ export const AUTH_ERR = Object.freeze({ 'Error during user authentication re-validation (Error: AUTH_032). Please log in again.', }) +export const AXIOS_INST_ERR = Object.freeze({ + SESSION_INACTIVE: + 'Your session is not active. Please log out and try logging in again.', +}) + export const MSG_DIALOG_TITLE = Object.freeze({ DATA_DUPLICATED: 'Data Duplicated!', DATA_INCOMPLETE: 'Data Incomplete!', @@ -50,10 +55,6 @@ export const MSG_DIALOG_TITLE = Object.freeze({ export const MDL_PRM_INPUT_HINT = Object.freeze({ SITE_ZERO_NOT_KNOW: 'A value of zero indicates not known.', SITE_DFT_COMPUTED: 'A default will be computed when the model is run.', - DENSITY_WO_AGE: 'Density Measurements cannot be supplied without an Age.', - DENSITY_PCC_APPLY_DFT: 'Applying Default of 50%', - ATTR_REQ_AGE_BSL_AREA: - 'These additional Stand attributes require that a Stand Age and Basal Area be supplied on the Site Index and the Density pages', }) export const MDL_PRM_INPUT_ERR = Object.freeze({ @@ -71,47 +72,11 @@ export const MDL_PRM_INPUT_ERR = Object.freeze({ SPCZ_VLD_MISSING_DERIVED_BY: "Input field - 'Species % derived by' - is missing essential information which must be filled in order to confirm and continue", SPCZ_VLD_TOTAL_PCT_NOT_100: 'Species Percent do not total 100.0%', - SITE_VLD_PCT_STCB_AREA_RNG: - "'Percent Stockable Area' must range from 0 and 100", - SITE_VLD_AGE_RNG: "'Stand Age' must range from 0 and 500", - SITE_VLD_HIGHT_RNG: "'Stand Height' must range from 0.00 and 99.90", SITE_VLD_SI_RNG: "'Site Index' must range from 0.00 and 60.00", - SITE_VLD_SPCZ_REQ_VALS_SUP: (selectedSiteSpeciesValue: string | null) => - `The species '${selectedSiteSpeciesValue}' must have Age/Height/BHA 50 Site Index values supplied`, SITE_VLD_SPCZ_REQ_SI_VAL: (selectedSiteSpeciesValue: string | null) => `The species '${selectedSiteSpeciesValue}' must have an BHA 50 Site Index value supplied`, - DENSITY_VLD_BSL_AREA_RNG: "'Basal Area' must range from 0.1000 and 250.0000", - DENSITY_VLD_TPH_RNG: "'Trees per Hectare' must range from 0.10 and 9999.90", - DENSITY_VLD_PCC_RNG: "'Crown Closure' must range from 0 and 100", - DENSITY_VLD_BSL_AREA_OVER_HEIGHT: - 'Basal Area is above a likely maximum for the entered height. Do you wish to proceed?', - ATTR_VLD_FLDS: (fieldName: string) => `${fieldName}: is not a valid number`, - ATTR_VLD_NO_MODIFY: - "At least one of the starting values must have been modified from the original computed values.\n\n Please modify at least one starting value or switch to 'Computed Values' mode", - ATTR_VLD_COMP_BSL_AREA: (basalAreaValue: string) => - `'Basal Area - 12.5cm+' is greater than the Basal Area defined on the Stand Density Pane.\n\n 'Basal Area - 7.5cm+' on Stand Density Pane: ${basalAreaValue}`, - ATTR_VLD_COMP_WSV: - "'Whole Stem Volume - 12.5cm+': is greater than 'Whole Stem Volume - 7.5cm+'", - ATTR_VLD_COMP_CUV: - "'Close Utilization Volume - 12.5cm+': is greater than 'Whole Stem Volume - 12.5cm+'", - ATTR_VLD_COMP_CUNDV: - "'Close Utilization Net Decay Volume - 12.5cm+': is greater than 'Close Utilization Volume - 12.5cm+'", - ATTR_VLD_COMP_CUNDWV: - "'Close Utilization Net Decay Waste Volume - 12.5cm+': is greater than 'Close Utilization Net Decay Volume - 12.5cm+'", - ATTR_VLD_LRY_HEIGHT_RNG: - "'Lorey Height - 7.5cm+': must range from 0.01 and 99.90", - ATTR_VLD_WSV75_RNG: - "'Whole Stem Volume - 7.5cm+': must range from 0.1 and 2500.0", - ATTR_VLD_BSL_AREA_RNG: - "'Basal Area - 12.5cm+': must range from 0.1000 and 250.0000", - ATTR_VLD_WSV125_RNG: - "'Whole Stem Volume - 12.5cm+': must range from 0.0 and 2500.0", - ATTR_VLD_CUV_RNG: - "'Close Utilization Volume - 12.5cm+': must range from 0.0 and 2500.0", - ATTR_VLD_CUNDV_RNG: - "'Close Utilization Net Decay Volume - 12.5cm+': must range from 0.0 and 2500.0", - ATTR_VLD_CUNDWV_RNG: - "'Close Utilization Net Decay Waste Volume - 12.5cm+': must range from 0.0 and 2500.0", + DENSITY_VLD_PCT_STCB_AREA_RNG: + "'Percent Stockable Area' must range from 0 and 100", RPT_VLD_COMP_FNSH_AGE: "'Finish Age' must be at least as great as the 'Start Age'", RPT_VLD_START_AGE_RNG: (startAgeMin: number, startAgeMax: number) => @@ -123,10 +88,17 @@ export const MDL_PRM_INPUT_ERR = Object.freeze({ }) export const FILE_UPLOAD_ERR = Object.freeze({ - LAYER_FILE_MISSING: 'Please provide a layer file.', - POLYGON_FILE_MISSING: 'Please provide a polygon file.', + LAYER_FILE_MISSING: 'Layer file is missing. Please upload the required file.', + POLYGON_FILE_MISSING: + 'Polygon file is missing. Please upload the required file.', LAYER_FILE_NOT_CSV_FORMAT: - 'The layer file is not in the correct CSV file format, please check and re-upload.', + 'The uploaded Layer file is not in CSV format. Please upload a valid CSV file.', POLYGON_FILE_NOT_CSV_FORMAT: - 'The polygon file is not in the correct CSV file format, please check and re-upload.', + 'The uploaded Polygon file is not in CSV format. Please upload a valid CSV file.', + RPT_VLD_REQUIRED_FIELDS: + 'All required fields (Starting Age, Finishing Age, Age Increment) must be filled.', + MISSING_RESPONSED_FILE: + 'The response is missing one or more required files. Please contact support or try again later.', + INVALID_RESPONSED_FILE: + 'The response contains invalid or corrupted files. Please contact support or try again later.', }) diff --git a/frontend/src/constants/options.ts b/frontend/src/constants/options.ts index 22fe1783f..465d946be 100644 --- a/frontend/src/constants/options.ts +++ b/frontend/src/constants/options.ts @@ -23,15 +23,9 @@ export const becZoneOptions = [ ] export const siteSpeciesValuesOptions = [ - { label: 'Computed', value: CONSTANTS.SITE_SPECIES_VALUES.COMPUTED }, { label: 'Supplied', value: CONSTANTS.SITE_SPECIES_VALUES.SUPPLIED }, ] -export const ageTypeOptions = [ - { label: 'Total', value: CONSTANTS.AGE_TYPE.TOTAL }, - { label: 'Breast', value: CONSTANTS.AGE_TYPE.BREAST }, -] - export const ecoZoneOptions = [ { label: 'Boreal Cordillera', value: '1' }, { label: 'Boreal Plains', value: '2' }, @@ -40,28 +34,6 @@ export const ecoZoneOptions = [ { label: 'Taiga Plains', value: '5' }, ] -export const floatingOptions = [ - { label: 'Float', value: CONSTANTS.FLOATING.AGE }, - { label: 'Float', value: CONSTANTS.FLOATING.HEIGHT }, - { label: 'Float', value: CONSTANTS.FLOATING.SITEINDEX }, -] - -export const minimumDBHLimitsOptions = [ - { label: '4.0 cm+', value: CONSTANTS.MINIMUM_DBH_LIMITS.CM4_0 }, - { label: '7.5 cm+', value: CONSTANTS.MINIMUM_DBH_LIMITS.CM7_5 }, - { label: '12.5 cm+', value: CONSTANTS.MINIMUM_DBH_LIMITS.CM12_5 }, - { label: '17.5 cm+', value: CONSTANTS.MINIMUM_DBH_LIMITS.CM17_5 }, - { label: '22.5 cm+', value: CONSTANTS.MINIMUM_DBH_LIMITS.CM22_5 }, -] - -export const addtStandAttrsOptions = [ - { - label: 'Use Computed Values', - value: CONSTANTS.COMPUTED_VALUES.USE, - }, - { label: 'Modify Computed Values', value: CONSTANTS.COMPUTED_VALUES.MODIFY }, -] - export const volumeReportedOptions = [ { label: 'Whole Stem', value: CONSTANTS.VOLUME_REPORTED.WHOLE_STEM }, { label: 'Close Utilization', value: CONSTANTS.VOLUME_REPORTED.CLOSE_UTIL }, @@ -87,7 +59,7 @@ export const projectionTypeOptions = [ { label: 'CFS Biomass', value: CONSTANTS.PROJECTION_TYPE.CFS_BIOMASS }, ] -export const modelTypeOptions = [ +export const modelSelectionOptions = [ { label: 'File Upload', value: CONSTANTS.MODEL_SELECTION.FILE_UPLOAD, @@ -97,14 +69,3 @@ export const modelTypeOptions = [ value: CONSTANTS.MODEL_SELECTION.INPUT_MODEL_PARAMETERS, }, ] - -export const engineVersionOptions = [ - { - label: 'VDYP 8', - value: CONSTANTS.ENGINE_VERSION.VDYP8, - }, - { - label: 'VDYP 9', - value: CONSTANTS.ENGINE_VERSION.VDYP9, - }, -] diff --git a/frontend/src/models/code.ts b/frontend/src/models/code.ts deleted file mode 100644 index 581cb1a3b..000000000 --- a/frontend/src/models/code.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { env } from '@/env' -export default class Code { - public codeTableName: string - public codeName: string - public description: string - public displayOrder: number - public effectiveDate: Date | null - public expiryDate: Date | null - public etag: number | null - public readonly '@type' = env.VITE_CODE_TYPE_URL - - constructor(blob: any) { - this.codeTableName = - blob && typeof blob.codeTableName === 'string' ? blob.codeTableName : '' - this.codeName = - blob && typeof blob.codeName === 'string' ? blob.codeName : '' - this.description = - blob && typeof blob.description === 'string' ? blob.description : '' - this.displayOrder = - blob && typeof blob.displayOrder === 'number' ? blob.displayOrder : 0 - this.effectiveDate = - blob && blob.effectiveDate ? new Date(blob.effectiveDate) : null - this.expiryDate = blob && blob.expiryDate ? new Date(blob.expiryDate) : null - this.etag = blob && typeof blob.etag === 'number' ? blob.etag : null - } -} diff --git a/frontend/src/router/routes.ts b/frontend/src/router/routes.ts index 1d972a5a6..2b247b1cc 100644 --- a/frontend/src/router/routes.ts +++ b/frontend/src/router/routes.ts @@ -2,7 +2,6 @@ import type { RouteRecordRaw } from 'vue-router' import ModelParameterInput from '@/views/input-model-parameters/ModelParameterInput.vue' import PageNotFound from '@/views/PageNotFound.vue' import AuthInfo from '@/views/test/AuthInfo.vue' -import APITest from '@/views/test/APITest.vue' export const routes: Array = [ { @@ -15,10 +14,5 @@ export const routes: Array = [ name: 'AuthInfo', component: AuthInfo, }, - { - path: '/api-test', - name: 'APITest', - component: APITest, - }, { path: '/:pathMatch(.*)*', name: 'NotFound', component: PageNotFound }, ] diff --git a/frontend/src/services/apiActions.ts b/frontend/src/services/apiActions.ts new file mode 100644 index 000000000..5ff63fc7d --- /dev/null +++ b/frontend/src/services/apiActions.ts @@ -0,0 +1,31 @@ +import apiClient from '@/services/apiClient' + +export const helpGet = async (): Promise => { + try { + const response = await apiClient.helpGet() + return response.data + } catch (error) { + console.error('Error fetching help details:', error) + throw error + } +} + +export const projectionHcsvPost = async (body: any): Promise => { + try { + const response = await apiClient.projectionHcsvPost(body) + return response.data + } catch (error) { + console.error('Error running projection:', error) + throw error + } +} + +export const rootGet = async (): Promise => { + try { + const response = await apiClient.rootGet() + return response.data + } catch (error) { + console.error('Error fetching root details:', error) + throw error + } +} diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts new file mode 100644 index 000000000..0727ae10b --- /dev/null +++ b/frontend/src/services/apiClient.ts @@ -0,0 +1,30 @@ +import { + HelpEndpointApi, + ProjectionEndpointApi, + RootEndpointApi, +} from '@/services/vdyp-api/' +import axiosInstance from '@/services/axiosInstance' + +const helpApiInstance = new HelpEndpointApi(undefined, undefined, axiosInstance) +const projectionApiInstance = new ProjectionEndpointApi( + undefined, + undefined, + axiosInstance, +) +const rootApiInstance = new RootEndpointApi(undefined, undefined, axiosInstance) + +export const apiClient = { + helpGet: (options?: any) => { + return helpApiInstance.v8HelpGet(options) + }, + + projectionHcsvPost: (body?: any, options?: any) => { + return projectionApiInstance.v8ProjectionHcsvPost(body, options) + }, + + rootGet: (options?: any) => { + return rootApiInstance.v8Get(options) + }, +} + +export default apiClient diff --git a/frontend/src/services/apiCreate.ts b/frontend/src/services/apiCreate.ts deleted file mode 100644 index b3b526862..000000000 --- a/frontend/src/services/apiCreate.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { post } from '@/services/apiService' -import Code from '@/models/code' - -export const code = async (code: Code): Promise => { - return post(`/codeTables/${code.codeTableName}`, code) -} - -export const uploadDcsvProjection = ( - projectionParameters: File, - inputData: File, -) => { - if (!projectionParameters || !inputData) { - throw new Error( - 'Invalid file input for projection parameters or input data.', - ) - } - - const formData = new FormData() - formData.append('projectionParameters', projectionParameters) - formData.append('inputData', inputData) - - return post('/projection/dcsv', formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - }) -} - -export const uploadHcsvProjection = ( - projectionParameters: Record, - layerFile: File, - polygonFile: File, -) => { - if (!projectionParameters || !layerFile || !polygonFile) { - throw new Error('Invalid input for projection parameters or files.') - } - - const formData = new FormData() - formData.append('projectionParameters', JSON.stringify(projectionParameters)) - formData.append('layerFile', layerFile) - formData.append('polygonFile', polygonFile) - return post('/projection/hcsv', formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - }) -} - -export const uploadScsvProjection = ( - projectionParameters: File, - polygonInputData: File, - layerInputData: File, - historyInputData: File, - nonVegetationInputData: File, - otherVegetationInputData: File, - polygonIdInputData: File, - speciesInputData: File, - vriAdjustInputData: File, -) => { - const files = [ - projectionParameters, - polygonInputData, - layerInputData, - historyInputData, - nonVegetationInputData, - otherVegetationInputData, - polygonIdInputData, - speciesInputData, - vriAdjustInputData, - ] - - for (const file of files) { - if (!file) { - throw new Error('One or more required files are missing.') - } - } - - const formData = new FormData() - formData.append('projectionParameters', projectionParameters) - formData.append('polygonInputData', polygonInputData) - formData.append('layerInputData', layerInputData) - formData.append('historyInputData', historyInputData) - formData.append('nonVegetationInputData', nonVegetationInputData) - formData.append('otherVegetationInputData', otherVegetationInputData) - formData.append('polygonIdInputData', polygonIdInputData) - formData.append('speciesInputData', speciesInputData) - formData.append('vriAdjustInputData', vriAdjustInputData) - - return post('/projection/scsv', formData, { - headers: { 'Content-Type': 'multipart/form-data' }, - }) -} diff --git a/frontend/src/services/apiDelete.ts b/frontend/src/services/apiDelete.ts deleted file mode 100644 index 189d9a1ed..000000000 --- a/frontend/src/services/apiDelete.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { del } from '@/services/apiService' - -export const code = async ( - codeTableName: string, - codeName: string, - ifMatch: string, -): Promise => { - if (!codeTableName) { - throw new Error('codeTableName is required.') - } - - if (!codeName) { - throw new Error('codeName is required.') - } - - if (!ifMatch) { - throw new Error('ifMatch is required.') - } - - const config = { - headers: { - 'If-Match': `"${ifMatch}"`, - }, - } - - return del(`/codeTables/${codeTableName}/codes/${codeName}`, config) -} diff --git a/frontend/src/services/apiFetch.ts b/frontend/src/services/apiFetch.ts deleted file mode 100644 index 1e2cfb004..000000000 --- a/frontend/src/services/apiFetch.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { get } from '@/services/apiService' -import { StatusCodes } from 'http-status-codes' -import Code from '@/models/code' - -export const topLevel = async (): Promise => { - const response = await get('/') - - if (!response || !response.status || !response.data) { - console.warn('Unexpected response format or status') - return null - } - - if (response.status === StatusCodes.OK) { - return response.data - } else { - return null - } -} - -export const pingServer = async (): Promise => { - const response = await get('/ping') - - if (!response || !response.status || !response.data) { - console.warn('Unexpected response format or status') - return null - } - - return response.status === StatusCodes.OK ? response.data : null -} - -export const code = async ( - codeTableName: string, - codeName: string, -): Promise => { - const response = await get( - `/codeTables/${codeTableName}/codes/${codeName}`, - ) - - if (!response || !response.status || !response.data || !response.headers) { - console.warn('Unexpected response format or status') - return null - } - - if (response.status === StatusCodes.OK) { - const codeJson = response.data - const etagQuoted = response.headers['etag'] - const etag = etagQuoted ? parseInt(etagQuoted.replace(/"/g, '')) : 0 - - return new Code({ ...codeJson, codeTableName, etagQuoted, etag }) - } else { - return null - } -} diff --git a/frontend/src/services/apiFetchAxios.ts b/frontend/src/services/apiFetchAxios.ts new file mode 100644 index 000000000..8cdca4cf3 --- /dev/null +++ b/frontend/src/services/apiFetchAxios.ts @@ -0,0 +1,44 @@ +import axiosInstance from './axiosInstance' +import { StatusCodes } from 'http-status-codes' + +export default class ApiFetchAxios { + static async getHelp(): Promise { + const response = await axiosInstance.get('/api/v8/help') + + if (!response || !response.status || !response.data) { + console.warn('Unexpected response format or status') + return null + } + + return response.status === StatusCodes.OK ? response.data : null + } + + static async projectionHcsvPost(): Promise { + const body = { + projectionParameters: { + startingAge: null, + finishingAge: null, + ageIncrement: null, + }, + layerInputData: null, + polygonInputData: null, + } + + const response = await axiosInstance.post( + '/api/v8/projection/hcsv', + body, + { + headers: { + 'Content-Type': 'application/json', + }, + responseType: 'blob', + }, + ) + + if (response.status === StatusCodes.CREATED) { + return response.data + } else { + throw new Error(`Unexpected status code: ${response.status}`) + } + } +} diff --git a/frontend/src/services/apiFetchDirect.ts b/frontend/src/services/apiFetchDirect.ts new file mode 100644 index 000000000..133df1d94 --- /dev/null +++ b/frontend/src/services/apiFetchDirect.ts @@ -0,0 +1,85 @@ +import { StatusCodes } from 'http-status-codes' +import { useAuthStore } from '@/stores/common/authStore' + +export default class ApiFetchDirect { + getRequest(path: string, headers: any): Promise { + return this.request(path, 'GET', null, headers) + } + + request( + path: string, + type: string, + body: any, + headers: any, + ): Promise { + const request: any = { + method: type, + headers: headers, + } + + if (body && (type === 'POST' || type === 'PUT')) { + request.body = JSON.stringify(body) + } + + console.log(`path: ${path}`) + console.log(`request: ${JSON.stringify(request)}`) + + return fetch(path, request) + } + + async getHelp(): Promise { + const apiUrl = '/api/v8/help' + const authStore = useAuthStore() + let token + if (authStore && authStore.user && authStore.user.accessToken) { + token = authStore.user.accessToken + } else { + console.warn('Authorization token or authStore is not available.') + } + console.log(token) + + const response = await this.getRequest(apiUrl, { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }) + + if (response.status === StatusCodes.OK) { + return response.json() + } else { + return null + } + } + + async projectionHcsvPost(): Promise { + const apiUrl = '/api/v8/projection/hcsv' + const authStore = useAuthStore() + let token + + if (authStore && authStore.user && authStore.user.accessToken) { + token = authStore.user.accessToken + } else { + console.warn('Authorization token or authStore is not available.') + } + + const body = { + projectionParameters: { + startingAge: null, + finishingAge: null, + ageIncrement: null, + }, + layerInputData: null, + polygonInputData: null, + } + + const response = await this.request(apiUrl, 'POST', body, { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }) + + if (response.status === StatusCodes.CREATED) { + return response.blob() + } else { + throw new Error(`Error: ${response.status} ${response.statusText}`) + } + } +} diff --git a/frontend/src/services/apiModules.ts b/frontend/src/services/apiModules.ts deleted file mode 100644 index 5b5878a50..000000000 --- a/frontend/src/services/apiModules.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as apiSearch from './apiSearch' -import * as apiFetch from './apiFetch' -import * as apiCreate from './apiCreate' -import * as apiUpdate from './apiUpdate' -import * as apiDelete from './apiDelete' - -export const API = { - search: apiSearch, - fetch: apiFetch, - create: apiCreate, - update: apiUpdate, - delete: apiDelete, -} diff --git a/frontend/src/services/apiSearch.ts b/frontend/src/services/apiSearch.ts deleted file mode 100644 index 2fcd6ce94..000000000 --- a/frontend/src/services/apiSearch.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { get } from '@/services/apiService' -import type { CodeSearchParams } from '@/interfaces/interfaces' - -function buildQueryString(params: Record): string { - const query = Object.entries(params) - .filter(([, value]) => value !== undefined && value !== null) - .map( - ([key, value]) => - `${encodeURIComponent(key)}=${encodeURIComponent(value)}`, - ) - .join('&') - - return query ? `?${query}` : '' -} - -export const code = async (param: CodeSearchParams): Promise => { - const queryString = buildQueryString({ - pageNumber: - typeof param.pageNumber === 'number' && param.pageNumber >= 0 - ? param.pageNumber - : 0, - pageSize: - typeof param.pageSize === 'number' && param.pageSize >= 0 - ? param.pageSize - : 10, - }) - - const response = await get(`/codeTables${queryString}`) - if (!response || !response.data) { - console.warn('Unexpected response format') - return null - } - - return response.data -} - -export const csvExport = async (): Promise => { - const response = await get(`/contactsExport?exportOption=All`, { - headers: { - Accept: 'text/csv', - }, - responseType: 'blob', - timeout: 5000, - }) - - if (!response || !response.data) { - console.warn('Unexpected response format') - return null - } - - return response.data -} diff --git a/frontend/src/services/apiService.ts b/frontend/src/services/apiService.ts deleted file mode 100644 index 00bafe3d9..000000000 --- a/frontend/src/services/apiService.ts +++ /dev/null @@ -1,32 +0,0 @@ -import axiosInstance from './axiosInstance' -import type { AxiosRequestConfig, AxiosResponse } from 'axios' - -export const get = async ( - url: string, - config?: AxiosRequestConfig, -): Promise> => { - return axiosInstance.get(url, config) -} - -export const post = async ( - url: string, - data: any, - config?: AxiosRequestConfig, -): Promise> => { - return axiosInstance.post(url, data, config) -} - -export const put = async ( - url: string, - data: any, - config?: AxiosRequestConfig, -): Promise> => { - return axiosInstance.put(url, data, config) -} - -export const del = async ( - url: string, - config?: AxiosRequestConfig, -): Promise> => { - return axiosInstance.delete(url, config) -} diff --git a/frontend/src/services/apiUpdate.ts b/frontend/src/services/apiUpdate.ts deleted file mode 100644 index 7e2f33fe0..000000000 --- a/frontend/src/services/apiUpdate.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { put } from '@/services/apiService' -import Code from '@/models/code' - -export const code = async (code: Code, ifMatch: string): Promise => { - if (!code || !code.codeTableName || !code.codeName) { - console.warn('Invalid Code object or missing required properties') - return null - } - - const config = { - headers: { - 'If-Match': `"${ifMatch}"`, - }, - } - - return put( - `/codeTables/${code.codeTableName}/codes/${code.codeName}`, - code, - config, - ) -} diff --git a/frontend/src/services/axiosInstance.ts b/frontend/src/services/axiosInstance.ts index ff54afe46..885600431 100644 --- a/frontend/src/services/axiosInstance.ts +++ b/frontend/src/services/axiosInstance.ts @@ -6,10 +6,10 @@ import type { } from 'axios' import { useAuthStore } from '@/stores/common/authStore' import { AXIOS } from '@/constants/constants' -import { env } from '@/env' +import { logErrorMessage } from '@/utils/messageHandler' +import { AXIOS_INST_ERR } from '@/constants/message' const axiosInstance: AxiosInstance = axios.create({ - baseURL: env.VITE_API_BASE_URL, headers: { Accept: AXIOS.ACCEPT, 'Content-Type': AXIOS.CONTENT_TYPE, @@ -30,7 +30,10 @@ axiosInstance.interceptors.request.use( config.headers.Authorization = `Bearer ${token}` } } else { - console.warn('Authorization token or authStore is not available.') + logErrorMessage( + AXIOS_INST_ERR.SESSION_INACTIVE, + 'Authorization token or authStore is not available.', + ) } return config diff --git a/frontend/src/services/vdyp-api/api.ts b/frontend/src/services/vdyp-api/api.ts new file mode 100644 index 000000000..97f4bc0c5 --- /dev/null +++ b/frontend/src/services/vdyp-api/api.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './apis/help-endpoint-api' +export * from './apis/projection-endpoint-api' +export * from './apis/root-endpoint-api' diff --git a/frontend/src/services/vdyp-api/apis/help-endpoint-api.ts b/frontend/src/services/vdyp-api/apis/help-endpoint-api.ts new file mode 100644 index 000000000..994f64b1d --- /dev/null +++ b/frontend/src/services/vdyp-api/apis/help-endpoint-api.ts @@ -0,0 +1,151 @@ +/* tslint:disable */ +/* eslint-disable */ +import globalAxios from 'axios' +import type { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios' +import { Configuration } from '../configuration' +// Some imports not used depending on template conditions +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from '../base' +import type { RequestArgs } from '../base' +import type { ParameterDetailsMessage } from '../models' + +/** + * HelpEndpointApi - axios parameter creator + * @export + */ +export const HelpEndpointApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + v8HelpGet: async ( + options: AxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/api/v8/help` /* edited */ + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com') + let baseOptions + if (configuration) { + baseOptions = configuration.baseOptions + } + const localVarRequestOptions: AxiosRequestConfig = { + method: 'GET', + ...baseOptions, + ...options, + responseType: 'json' /* edited */, + } + const localVarHeaderParameter = {} as any + const localVarQueryParameter = {} as any + + const query = new URLSearchParams(localVarUrlObj.search) + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]) + } + for (const key in options.params) { + query.set(key, options.params[key]) + } + localVarUrlObj.search = new URLSearchParams(query).toString() + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {} + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + } + + return { + url: + localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + } + }, + } +} + +/** + * HelpEndpointApi - functional programming interface + * @export + */ +export const HelpEndpointApiFp = function (configuration?: Configuration) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8HelpGet( + options?: AxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => Promise> + > { + const localVarAxiosArgs = + await HelpEndpointApiAxiosParamCreator(configuration).v8HelpGet(options) + return ( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs: AxiosRequestConfig = { + ...localVarAxiosArgs.options, + url: /* edited */ localVarAxiosArgs.url, + } + return axios.request( + axiosRequestArgs, + ) + } + }, + } +} + +/** + * HelpEndpointApi - factory interface + * @export + */ +export const HelpEndpointApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8HelpGet( + options?: AxiosRequestConfig, + ): Promise> { + return HelpEndpointApiFp(configuration) + .v8HelpGet(options) + .then((request) => request(axios /* edited */)) + }, + } +} + +/** + * HelpEndpointApi - object-oriented interface + * @export + * @class HelpEndpointApi + * @extends {BaseAPI} + */ +export class HelpEndpointApi extends BaseAPI { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof HelpEndpointApi + */ + public async v8HelpGet( + options?: AxiosRequestConfig, + ): Promise> { + return HelpEndpointApiFp(this.configuration) + .v8HelpGet(options) + .then((request) => request(this.axios /* edited */)) + } +} diff --git a/frontend/src/services/vdyp-api/apis/projection-endpoint-api.ts b/frontend/src/services/vdyp-api/apis/projection-endpoint-api.ts new file mode 100644 index 000000000..24aa183ae --- /dev/null +++ b/frontend/src/services/vdyp-api/apis/projection-endpoint-api.ts @@ -0,0 +1,395 @@ +/* tslint:disable */ +/* eslint-disable */ +import globalAxios from 'axios' +import type { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios' +import { Configuration } from '../configuration' +// Some imports not used depending on template conditions +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from '../base' +import type { RequestArgs } from '../base' +import type { + ProjectionDcsvPostRequest, + ProjectionHcsvPostRequest, + ProjectionScsvPostRequest, +} from '../models' + +/** + * ProjectionEndpointApi - axios parameter creator + * @export + */ +export const ProjectionEndpointApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {ProjectionDcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + v8ProjectionDcsvPost: async ( + body?: ProjectionDcsvPostRequest, + options: AxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/v8/projection/dcsv` + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com') + let baseOptions + if (configuration) { + baseOptions = configuration.baseOptions + } + const localVarRequestOptions: AxiosRequestConfig = { + method: 'POST', + ...baseOptions, + ...options, + } + const localVarHeaderParameter = {} as any + const localVarQueryParameter = {} as any + + localVarHeaderParameter['Content-Type'] = 'application/json' + + const query = new URLSearchParams(localVarUrlObj.search) + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]) + } + for (const key in options.params) { + query.set(key, options.params[key]) + } + localVarUrlObj.search = new URLSearchParams(query).toString() + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {} + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + } + const needsSerialization = + typeof body !== 'string' || + (localVarRequestOptions.headers && + localVarRequestOptions.headers['Content-Type'] === 'application/json') + localVarRequestOptions.data = needsSerialization + ? JSON.stringify(body !== undefined ? body : {}) + : body || '' + + return { + url: + localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + } + }, + /** + * + * @param {ProjectionHcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + v8ProjectionHcsvPost: async ( + body?: ProjectionHcsvPostRequest, + options: AxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/api/v8/projection/hcsv` /* edited */ + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com') + let baseOptions + if (configuration) { + baseOptions = configuration.baseOptions + } + const localVarRequestOptions: AxiosRequestConfig = { + method: 'POST', + ...baseOptions, + ...options, + responseType: 'blob' /* edited */, + } + const localVarHeaderParameter = {} as any + const localVarQueryParameter = {} as any + + localVarHeaderParameter['Content-Type'] = 'application/json' + + const query = new URLSearchParams(localVarUrlObj.search) + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]) + } + for (const key in options.params) { + query.set(key, options.params[key]) + } + localVarUrlObj.search = new URLSearchParams(query).toString() + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {} + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + } + const needsSerialization = + typeof body !== 'string' || + (localVarRequestOptions.headers && + localVarRequestOptions.headers['Content-Type'] === 'application/json') + localVarRequestOptions.data = needsSerialization + ? JSON.stringify(body !== undefined ? body : {}) + : body || '' + + return { + url: + localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + } + }, + /** + * + * @param {ProjectionScsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + v8ProjectionScsvPost: async ( + body?: ProjectionScsvPostRequest, + options: AxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/v8/projection/scsv` + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com') + let baseOptions + if (configuration) { + baseOptions = configuration.baseOptions + } + const localVarRequestOptions: AxiosRequestConfig = { + method: 'POST', + ...baseOptions, + ...options, + } + const localVarHeaderParameter = {} as any + const localVarQueryParameter = {} as any + + localVarHeaderParameter['Content-Type'] = 'application/json' + + const query = new URLSearchParams(localVarUrlObj.search) + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]) + } + for (const key in options.params) { + query.set(key, options.params[key]) + } + localVarUrlObj.search = new URLSearchParams(query).toString() + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {} + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + } + const needsSerialization = + typeof body !== 'string' || + (localVarRequestOptions.headers && + localVarRequestOptions.headers['Content-Type'] === 'application/json') + localVarRequestOptions.data = needsSerialization + ? JSON.stringify(body !== undefined ? body : {}) + : body || '' + + return { + url: + localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + } + }, + } +} + +/** + * ProjectionEndpointApi - functional programming interface + * @export + */ +export const ProjectionEndpointApiFp = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {ProjectionDcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8ProjectionDcsvPost( + body?: ProjectionDcsvPostRequest, + options?: AxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => Promise> + > { + const localVarAxiosArgs = await ProjectionEndpointApiAxiosParamCreator( + configuration, + ).v8ProjectionDcsvPost(body, options) + return ( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs: AxiosRequestConfig = { + ...localVarAxiosArgs.options, + url: basePath + localVarAxiosArgs.url, + } + return axios.request(axiosRequestArgs) + } + }, + /** + * + * @param {ProjectionHcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8ProjectionHcsvPost( + body?: ProjectionHcsvPostRequest, + options?: AxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => Promise> /* edited */ + > { + const localVarAxiosArgs = await ProjectionEndpointApiAxiosParamCreator( + configuration, + ).v8ProjectionHcsvPost(body, options) + return ( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs: AxiosRequestConfig = { + ...localVarAxiosArgs.options, + url: /* edited */ localVarAxiosArgs.url, + } + return axios.request(axiosRequestArgs) + } + }, + /** + * + * @param {ProjectionScsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8ProjectionScsvPost( + body?: ProjectionScsvPostRequest, + options?: AxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => Promise> + > { + const localVarAxiosArgs = await ProjectionEndpointApiAxiosParamCreator( + configuration, + ).v8ProjectionScsvPost(body, options) + return ( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs: AxiosRequestConfig = { + ...localVarAxiosArgs.options, + url: basePath + localVarAxiosArgs.url, + } + return axios.request(axiosRequestArgs) + } + }, + } +} + +/** + * ProjectionEndpointApi - factory interface + * @export + */ +export const ProjectionEndpointApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + return { + /** + * + * @param {ProjectionDcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8ProjectionDcsvPost( + body?: ProjectionDcsvPostRequest, + options?: AxiosRequestConfig, + ): Promise> { + return ProjectionEndpointApiFp(configuration) + .v8ProjectionDcsvPost(body, options) + .then((request) => request(axios, basePath)) + }, + /** + * + * @param {ProjectionHcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8ProjectionHcsvPost( + body?: ProjectionHcsvPostRequest, + options?: AxiosRequestConfig, + ): Promise> { + return ProjectionEndpointApiFp(configuration) + .v8ProjectionHcsvPost(body, options) + .then((request) => request(axios /* edited */)) + }, + /** + * + * @param {ProjectionScsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8ProjectionScsvPost( + body?: ProjectionScsvPostRequest, + options?: AxiosRequestConfig, + ): Promise> { + return ProjectionEndpointApiFp(configuration) + .v8ProjectionScsvPost(body, options) + .then((request) => request(axios, basePath)) + }, + } +} + +/** + * ProjectionEndpointApi - object-oriented interface + * @export + * @class ProjectionEndpointApi + * @extends {BaseAPI} + */ +export class ProjectionEndpointApi extends BaseAPI { + /** + * + * @param {ProjectionDcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ProjectionEndpointApi + */ + public async v8ProjectionDcsvPost( + body?: ProjectionDcsvPostRequest, + options?: AxiosRequestConfig, + ): Promise> { + return ProjectionEndpointApiFp(this.configuration) + .v8ProjectionDcsvPost(body, options) + .then((request) => request(this.axios, this.basePath)) + } + /** + * + * @param {ProjectionHcsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ProjectionEndpointApi + */ + public async v8ProjectionHcsvPost( + body?: ProjectionHcsvPostRequest, + options?: AxiosRequestConfig, + ): Promise> /* edited */ { + return ProjectionEndpointApiFp(this.configuration) + .v8ProjectionHcsvPost(body, options) + .then((request) => request(this.axios /* edited */)) + } + /** + * + * @param {ProjectionScsvPostRequest} [body] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ProjectionEndpointApi + */ + public async v8ProjectionScsvPost( + body?: ProjectionScsvPostRequest, + options?: AxiosRequestConfig, + ): Promise> { + return ProjectionEndpointApiFp(this.configuration) + .v8ProjectionScsvPost(body, options) + .then((request) => request(this.axios, this.basePath)) + } +} diff --git a/frontend/src/services/vdyp-api/apis/root-endpoint-api.ts b/frontend/src/services/vdyp-api/apis/root-endpoint-api.ts new file mode 100644 index 000000000..874bd6460 --- /dev/null +++ b/frontend/src/services/vdyp-api/apis/root-endpoint-api.ts @@ -0,0 +1,139 @@ +/* tslint:disable */ +/* eslint-disable */ +import globalAxios from 'axios' +import type { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios' +import { Configuration } from '../configuration' +// Some imports not used depending on template conditions +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError } from '../base' +import type { RequestArgs } from '../base' +/** + * RootEndpointApi - axios parameter creator + * @export + */ +export const RootEndpointApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + v8Get: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v8` /* edited */ + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, 'https://example.com') + let baseOptions + if (configuration) { + baseOptions = configuration.baseOptions + } + const localVarRequestOptions: AxiosRequestConfig = { + method: 'GET', + ...baseOptions, + ...options, + } + const localVarHeaderParameter = {} as any + const localVarQueryParameter = {} as any + + const query = new URLSearchParams(localVarUrlObj.search) + for (const key in localVarQueryParameter) { + query.set(key, localVarQueryParameter[key]) + } + for (const key in options.params) { + query.set(key, options.params[key]) + } + localVarUrlObj.search = new URLSearchParams(query).toString() + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {} + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + } + + return { + url: + localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash, + options: localVarRequestOptions, + } + }, + } +} + +/** + * RootEndpointApi - functional programming interface + * @export + */ +export const RootEndpointApiFp = function (configuration?: Configuration) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8Get( + options?: AxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => Promise> + > { + const localVarAxiosArgs = + await RootEndpointApiAxiosParamCreator(configuration).v8Get(options) + return ( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs: AxiosRequestConfig = { + ...localVarAxiosArgs.options, + url: /* edited */ localVarAxiosArgs.url, + } + return axios.request(axiosRequestArgs) + } + }, + } +} + +/** + * RootEndpointApi - factory interface + * @export + */ +export const RootEndpointApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async v8Get(options?: AxiosRequestConfig): Promise> { + return RootEndpointApiFp(configuration) + .v8Get(options) + .then((request) => request(axios /* edited */)) + }, + } +} + +/** + * RootEndpointApi - object-oriented interface + * @export + * @class RootEndpointApi + * @extends {BaseAPI} + */ +export class RootEndpointApi extends BaseAPI { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RootEndpointApi + */ + public async v8Get( + options?: AxiosRequestConfig, + ): Promise> { + return RootEndpointApiFp(this.configuration) + .v8Get(options) + .then((request) => request(this.axios /* edited */)) + } +} diff --git a/frontend/src/services/vdyp-api/base.ts b/frontend/src/services/vdyp-api/base.ts new file mode 100644 index 000000000..94cb189a0 --- /dev/null +++ b/frontend/src/services/vdyp-api/base.ts @@ -0,0 +1,67 @@ +/* tslint:disable */ +/* eslint-disable */ +import { Configuration } from './configuration' +// Some imports not used depending on template conditions +// @ts-ignore +import globalAxios from 'axios' +import type { AxiosRequestConfig, AxiosInstance } from 'axios' +import { env } from '@/env' + +export const BASE_PATH = env.VITE_API_URL.replace(/\/+$/, '') + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ',', + ssv: ' ', + tsv: '\t', + pipes: '|', +} + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string + options: AxiosRequestConfig +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined + + constructor( + configuration?: Configuration, + protected basePath: string = BASE_PATH, + protected axios: AxiosInstance = globalAxios, + ) { + if (configuration) { + this.configuration = configuration + this.basePath = configuration.basePath || this.basePath + } + } +} + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + name: 'RequiredError' = 'RequiredError' + constructor( + public field: string, + msg?: string, + ) { + super(msg) + } +} diff --git a/frontend/src/services/vdyp-api/configuration.ts b/frontend/src/services/vdyp-api/configuration.ts new file mode 100644 index 000000000..d146482ee --- /dev/null +++ b/frontend/src/services/vdyp-api/configuration.ts @@ -0,0 +1,86 @@ +/* tslint:disable */ +/* eslint-disable */ +export interface ConfigurationParameters { + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise) + username?: string + password?: string + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise) + basePath?: string + baseOptions?: any +} + +export class Configuration { + /** + * parameter for apiKey security + * + * @param name security name + * @memberof Configuration + */ + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise) + + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string + + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string + + /** + * parameter for oauth2 security + * + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise) + + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string + + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey + this.username = param.username + this.password = param.password + this.accessToken = param.accessToken + this.basePath = param.basePath + this.baseOptions = param.baseOptions + } +} diff --git a/frontend/src/services/vdyp-api/index.ts b/frontend/src/services/vdyp-api/index.ts new file mode 100644 index 000000000..5e6ba1006 --- /dev/null +++ b/frontend/src/services/vdyp-api/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './api' +export * from './configuration' +export * from './models' diff --git a/frontend/src/services/vdyp-api/models/combine-age-year-range-enum.ts b/frontend/src/services/vdyp-api/models/combine-age-year-range-enum.ts new file mode 100644 index 000000000..a5fcd834e --- /dev/null +++ b/frontend/src/services/vdyp-api/models/combine-age-year-range-enum.ts @@ -0,0 +1,7 @@ +/* tslint:disable */ +/* eslint-disable */ +export enum CombineAgeYearRangeEnum { + Union = 'union', + Intersect = 'intersect', + Difference = 'difference', +} diff --git a/frontend/src/services/vdyp-api/models/filters.ts b/frontend/src/services/vdyp-api/models/filters.ts new file mode 100644 index 000000000..e31a18a7e --- /dev/null +++ b/frontend/src/services/vdyp-api/models/filters.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +export interface Filters { + /** + * @type {string} + * @memberof Filters + */ + maintainer?: string + + /** + * @type {string} + * @memberof Filters + */ + mapsheet?: string + + /** + * @type {string} + * @memberof Filters + */ + polygon?: string + + /** + * @type {string} + * @memberof Filters + */ + polygonId?: string +} diff --git a/frontend/src/services/vdyp-api/models/index.ts b/frontend/src/services/vdyp-api/models/index.ts new file mode 100644 index 000000000..e0c0ac108 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/index.ts @@ -0,0 +1,14 @@ +export * from './combine-age-year-range-enum' +export * from './filters' +export * from './metadata-to-output-enum' +export * from './output-format-enum' +export * from './parameters' +export * from './parameter-details-message' /* added */ +export * from './parameters-progress-frequency' +export * from './parameters-utils-inner' +export * from './projection-dcsv-post-request' +export * from './projection-hcsv-post-request' +export * from './projection-scsv-post-request' +export * from './selected-debug-options-enum' +export * from './selected-execution-options-enum' +export * from './value-enum' diff --git a/frontend/src/services/vdyp-api/models/metadata-to-output-enum.ts b/frontend/src/services/vdyp-api/models/metadata-to-output-enum.ts new file mode 100644 index 000000000..331614ba3 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/metadata-to-output-enum.ts @@ -0,0 +1,9 @@ +/* tslint:disable */ +/* eslint-disable */ +export enum MetadataToOutputEnum { + ALL = 'ALL', + MAIN = 'MAIN', + VERSION = 'VERSION', + MINIDENT = 'MIN_IDENT', + NONE = 'NONE', +} diff --git a/frontend/src/services/vdyp-api/models/output-format-enum.ts b/frontend/src/services/vdyp-api/models/output-format-enum.ts new file mode 100644 index 000000000..a7baa29aa --- /dev/null +++ b/frontend/src/services/vdyp-api/models/output-format-enum.ts @@ -0,0 +1,7 @@ +/* tslint:disable */ +/* eslint-disable */ +export enum OutputFormatEnum { + YieldTable = 'YieldTable', + CSVYieldTable = 'CSVYieldTable', + DCSV = 'DCSV', +} diff --git a/frontend/src/services/vdyp-api/models/parameter-details-message.ts b/frontend/src/services/vdyp-api/models/parameter-details-message.ts new file mode 100644 index 000000000..29a6d71e9 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/parameter-details-message.ts @@ -0,0 +1,43 @@ +/* tslint:disable */ +/* eslint-disable */ +export interface ParameterDetailsMessage { + /** + * the parameter name + * + * @type {string} + * @memberof ParameterDetailsMessage + */ + field?: string + + /** + * a brief description of the parameter's purpose + * + * @type {string} + * @memberof ParameterDetailsMessage + */ + shortDescription?: string + + /** + * if the parameter has a value, a description of the value + * + * @type {string} + * @memberof ParameterDetailsMessage + */ + parameterValue?: string + + /** + * a description of the parameter + * + * @type {string} + * @memberof ParameterDetailsMessage + */ + longDescription?: string + + /** + * the default value used if the parameter is not specified + * + * @type {string} + * @memberof ParameterDetailsMessage + */ + defaultValue?: string +} diff --git a/frontend/src/services/vdyp-api/models/parameters-progress-frequency.ts b/frontend/src/services/vdyp-api/models/parameters-progress-frequency.ts new file mode 100644 index 000000000..63164dd78 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/parameters-progress-frequency.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export interface ParametersProgressFrequency {} diff --git a/frontend/src/services/vdyp-api/models/parameters-utils-inner.ts b/frontend/src/services/vdyp-api/models/parameters-utils-inner.ts new file mode 100644 index 000000000..9ba95556b --- /dev/null +++ b/frontend/src/services/vdyp-api/models/parameters-utils-inner.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +import { ValueEnum } from './value-enum' +export interface ParametersUtilsInner { + /** + * @type {string} + * @memberof ParametersUtilsInner + */ + speciesName?: string + + /** + * @type {ValueEnum} + * @memberof ParametersUtilsInner + */ + value?: ValueEnum +} diff --git a/frontend/src/services/vdyp-api/models/parameters.ts b/frontend/src/services/vdyp-api/models/parameters.ts new file mode 100644 index 000000000..c6d83bde6 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/parameters.ts @@ -0,0 +1,149 @@ +/* tslint:disable */ +/* eslint-disable */ +import { CombineAgeYearRangeEnum } from './combine-age-year-range-enum' +import type { Filters } from './filters' +import { MetadataToOutputEnum } from './metadata-to-output-enum' +import { OutputFormatEnum } from './output-format-enum' +import type { ParametersProgressFrequency } from './parameters-progress-frequency' +import type { ParametersUtilsInner } from './parameters-utils-inner' +import { SelectedDebugOptionsEnum } from './selected-debug-options-enum' +import { SelectedExecutionOptionsEnum } from './selected-execution-options-enum' +export interface Parameters { + /** + * @type {OutputFormatEnum} + * @memberof Parameters + */ + outputFormat?: OutputFormatEnum + + /** + * @type {Array} + * @memberof Parameters + */ + selectedExecutionOptions?: Array + + /** + * @type {boolean} + * @memberof Parameters + */ + doEnableProgressLogging?: boolean + + /** + * @type {boolean} + * @memberof Parameters + */ + doEnableErrorLogging?: boolean + + /** + * @type {boolean} + * @memberof Parameters + */ + doEnableDebugLogging?: boolean + + /** + * @type {Array} + * @memberof Parameters + */ + selectedDebugOptions?: Array + + /** + * @type {number} + * @memberof Parameters + */ + ageStart?: number + + /** + * @type {number} + * @memberof Parameters + */ + minAgeStart?: number + + /** + * @type {number} + * @memberof Parameters + */ + maxAgeStart?: number + + /** + * @type {number} + * @memberof Parameters + */ + ageEnd?: number + + /** + * @type {number} + * @memberof Parameters + */ + minAgeEnd?: number + + /** + * @type {number} + * @memberof Parameters + */ + maxAgeEnd?: number + + /** + * @type {number} + * @memberof Parameters + */ + yearStart?: number + + /** + * @type {number} + * @memberof Parameters + */ + yearEnd?: number + + /** + * @type {number} + * @memberof Parameters + */ + forceYear?: number + + /** + * @type {number} + * @memberof Parameters + */ + ageIncrement?: number + + /** + * @type {number} + * @memberof Parameters + */ + minAgeIncrement?: number + + /** + * @type {number} + * @memberof Parameters + */ + maxAgeIncrement?: number + + /** + * @type {CombineAgeYearRangeEnum} + * @memberof Parameters + */ + combineAgeYearRange?: CombineAgeYearRangeEnum + + /** + * @type {ParametersProgressFrequency} + * @memberof Parameters + */ + progressFrequency?: ParametersProgressFrequency + + /** + * @type {MetadataToOutputEnum} + * @memberof Parameters + */ + metadataToOutput?: MetadataToOutputEnum + + /** + * @type {Filters} + * @memberof Parameters + */ + filters?: Filters + + /** + * @type {Array} + * @memberof Parameters + */ + utils?: Array +} diff --git a/frontend/src/services/vdyp-api/models/projection-dcsv-post-request.ts b/frontend/src/services/vdyp-api/models/projection-dcsv-post-request.ts new file mode 100644 index 000000000..856435343 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/projection-dcsv-post-request.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +import type { Parameters } from './parameters' +export interface ProjectionDcsvPostRequest { + /** + * @type {Parameters} + * @memberof ProjectionDcsvPostRequest + */ + projectionParameters?: Parameters + + /** + * @type {Blob} + * @memberof ProjectionDcsvPostRequest + */ + inputData?: Blob +} diff --git a/frontend/src/services/vdyp-api/models/projection-hcsv-post-request.ts b/frontend/src/services/vdyp-api/models/projection-hcsv-post-request.ts new file mode 100644 index 000000000..6fb9d22e0 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/projection-hcsv-post-request.ts @@ -0,0 +1,22 @@ +/* tslint:disable */ +/* eslint-disable */ +import type { Parameters } from './parameters' +export interface ProjectionHcsvPostRequest { + /** + * @type {Parameters} + * @memberof ProjectionHcsvPostRequest + */ + projectionParameters?: Parameters + + /** + * @type {Blob} + * @memberof ProjectionHcsvPostRequest + */ + polygonInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionHcsvPostRequest + */ + layerInputData?: Blob +} diff --git a/frontend/src/services/vdyp-api/models/projection-scsv-post-request.ts b/frontend/src/services/vdyp-api/models/projection-scsv-post-request.ts new file mode 100644 index 000000000..1756c2bbc --- /dev/null +++ b/frontend/src/services/vdyp-api/models/projection-scsv-post-request.ts @@ -0,0 +1,58 @@ +/* tslint:disable */ +/* eslint-disable */ +import type { Parameters } from './parameters' +export interface ProjectionScsvPostRequest { + /** + * @type {Parameters} + * @memberof ProjectionScsvPostRequest + */ + projectionParameters?: Parameters + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + polygonInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + layerInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + historyInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + nonVegetationInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + otherVegetationInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + polygonIdInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + speciesInputData?: Blob + + /** + * @type {Blob} + * @memberof ProjectionScsvPostRequest + */ + vriAdjustInputData?: Blob +} diff --git a/frontend/src/services/vdyp-api/models/selected-debug-options-enum.ts b/frontend/src/services/vdyp-api/models/selected-debug-options-enum.ts new file mode 100644 index 000000000..3b5dc889d --- /dev/null +++ b/frontend/src/services/vdyp-api/models/selected-debug-options-enum.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +/* eslint-disable */ +export enum SelectedDebugOptionsEnum { + DoIncludeDebugTimestamps = 'doIncludeDebugTimestamps', + DoIncludeDebugRoutineNames = 'doIncludeDebugRoutineNames', + DoIncludeDebugEntryExit = 'doIncludeDebugEntryExit', + DoIncludeDebugIndentBlocks = 'doIncludeDebugIndentBlocks', +} diff --git a/frontend/src/services/vdyp-api/models/selected-execution-options-enum.ts b/frontend/src/services/vdyp-api/models/selected-execution-options-enum.ts new file mode 100644 index 000000000..a0563bb96 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/selected-execution-options-enum.ts @@ -0,0 +1,24 @@ +/* tslint:disable */ +/* eslint-disable */ +export enum SelectedExecutionOptionsEnum { + BackGrowEnabled = 'backGrowEnabled', + ForwardGrowEnabled = 'forwardGrowEnabled', + DoSaveIntermediateFiles = 'doSaveIntermediateFiles', + DoForceReferenceYearInclusionInYieldTables = 'doForceReferenceYearInclusionInYieldTables', + DoForceCurrentYearInclusionInYieldTables = 'doForceCurrentYearInclusionInYieldTables', + DoForceCalendarYearInclusionInYieldTables = 'doForceCalendarYearInclusionInYieldTables', + DoIncludeFileHeader = 'doIncludeFileHeader', + DoIncludeProjectionModeInYieldTable = 'doIncludeProjectionModeInYieldTable', + DoIncludeAgeRowsInYieldTable = 'doIncludeAgeRowsInYieldTable', + DoIncludeYearRowsInYieldTable = 'doIncludeYearRowsInYieldTable', + DoIncludePolygonRecordIdInYieldTable = 'doIncludePolygonRecordIdInYieldTable', + DoSummarizeProjectionByPolygon = 'doSummarizeProjectionByPolygon', + DoSummarizeProjectionByLayer = 'doSummarizeProjectionByLayer', + DoIncludeSpeciesProjection = 'doIncludeSpeciesProjection', + DoIncludeProjectedMOFVolumes = 'doIncludeProjectedMOFVolumes', + DoIncludeProjectedMOFBiomass = 'doIncludeProjectedMOFBiomass', + DoIncludeProjectedCFSBiomass = 'doIncludeProjectedCFSBiomass', + DoIncludeColumnHeadersInYieldTable = 'doIncludeColumnHeadersInYieldTable', + DoAllowBasalAreaAndTreesPerHectareValueSubstitution = 'doAllowBasalAreaAndTreesPerHectareValueSubstitution', + DoIncludeSecondarySpeciesDominantHeightInYieldTable = 'doIncludeSecondarySpeciesDominantHeightInYieldTable', +} diff --git a/frontend/src/services/vdyp-api/models/value-enum.ts b/frontend/src/services/vdyp-api/models/value-enum.ts new file mode 100644 index 000000000..67388f166 --- /dev/null +++ b/frontend/src/services/vdyp-api/models/value-enum.ts @@ -0,0 +1,10 @@ +/* tslint:disable */ +/* eslint-disable */ +export enum ValueEnum { + Excl = 'Excl', + _40 = '4.0', + _75 = '7.5', + _125 = '12.5', + _175 = '17.5', + _225 = '22.5', +} diff --git a/frontend/src/stores/appStore.ts b/frontend/src/stores/appStore.ts index 8dce8fe63..05e1ac768 100644 --- a/frontend/src/stores/appStore.ts +++ b/frontend/src/stores/appStore.ts @@ -1,21 +1,17 @@ import { defineStore } from 'pinia' import { ref } from 'vue' -import { MODEL_SELECTION, ENGINE_VERSION } from '@/constants/constants' +import { MODEL_SELECTION } from '@/constants/constants' export const useAppStore = defineStore('appStore', () => { // Model Parameter Selection bar - const modelType = ref(MODEL_SELECTION.FILE_UPLOAD) - const engineVersion = ref(ENGINE_VERSION.VDYP8) - const jobId = ref('') + const modelSelection = ref(MODEL_SELECTION.FILE_UPLOAD) // Tabs const currentTab = ref(0) return { // Model Parameter Selection bar - modelType, - engineVersion, - jobId, + modelSelection, // Tabs currentTab, } diff --git a/frontend/src/stores/modelParameterStore.ts b/frontend/src/stores/modelParameterStore.ts index e4122c92d..780b78485 100644 --- a/frontend/src/stores/modelParameterStore.ts +++ b/frontend/src/stores/modelParameterStore.ts @@ -2,7 +2,6 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { PANEL, - FLOATING, MODEL_PARAMETER_PANEL, NUM_INPUT_LIMITS, } from '@/constants/constants' @@ -16,7 +15,6 @@ export const useModelParameterStore = defineStore('modelParameter', () => { speciesInfo: PANEL.OPEN, siteInfo: PANEL.CLOSE, standDensity: PANEL.CLOSE, - addtStandAttrs: PANEL.CLOSE, reportInfo: PANEL.CLOSE, }) @@ -24,14 +22,12 @@ export const useModelParameterStore = defineStore('modelParameter', () => { const panelState = ref< Record >({ - speciesInfo: { confirmed: false, editable: true }, // Only speciesInfo is editable initially + speciesInfo: { confirmed: false, editable: true }, siteInfo: { confirmed: false, editable: false }, standDensity: { confirmed: false, editable: false }, - addtStandAttrs: { confirmed: false, editable: false }, reportInfo: { confirmed: false, editable: false }, }) - // Run Model button state const runModelEnabled = ref(false) // @@ -57,7 +53,6 @@ export const useModelParameterStore = defineStore('modelParameter', () => { MODEL_PARAMETER_PANEL.SPECIES_INFO, MODEL_PARAMETER_PANEL.SITE_INFO, MODEL_PARAMETER_PANEL.STAND_DENSITY, - MODEL_PARAMETER_PANEL.ADDT_STAND_ATTRS, MODEL_PARAMETER_PANEL.REPORT_INFO, ] const currentIndex = panelOrder.indexOf(panelName) @@ -84,7 +79,6 @@ export const useModelParameterStore = defineStore('modelParameter', () => { MODEL_PARAMETER_PANEL.SPECIES_INFO, MODEL_PARAMETER_PANEL.SITE_INFO, MODEL_PARAMETER_PANEL.STAND_DENSITY, - MODEL_PARAMETER_PANEL.ADDT_STAND_ATTRS, MODEL_PARAMETER_PANEL.REPORT_INFO, ] const currentIndex = panelOrder.indexOf(panelName) @@ -119,7 +113,6 @@ export const useModelParameterStore = defineStore('modelParameter', () => { group: string percent: number siteSpecies: string - minimumDBHLimit: string }[] >([]) @@ -164,7 +157,6 @@ export const useModelParameterStore = defineStore('modelParameter', () => { group: key, percent: groupMap[key], siteSpecies: key, - minimumDBHLimit: DEFAULT_VALUES.MINIMUM_DBH_LIMIT, })) speciesGroups.value.sort((a, b) => b.percent - a.percent) @@ -178,31 +170,11 @@ export const useModelParameterStore = defineStore('modelParameter', () => { const becZone = ref(null) const ecoZone = ref(null) const incSecondaryHeight = ref(false) - const siteIndexCurve = ref(null) const siteSpeciesValues = ref(null) - const ageType = ref(null) - const percentStockableArea = ref(null) - const age = ref(null) - const height = ref(null) const bha50SiteIndex = ref(null) - const floating = ref(null) // stand density - const basalArea = ref(null) - const treesPerHectare = ref(null) - const minimumDBHLimit = ref(null) - const currentDiameter = ref(null) - const percentCrownClosure = ref(null) - - // additional stand attributes - const computedValues = ref(null) - const loreyHeight = ref(null) - const wholeStemVol75 = ref(null) - const basalArea125 = ref(null) - const wholeStemVol125 = ref(null) - const cuVol = ref(null) - const cuNetDecayVol = ref(null) - const cuNetDecayWasteVol = ref(null) + const percentStockableArea = ref(null) // report info const startingAge = ref(null) @@ -229,28 +201,12 @@ export const useModelParameterStore = defineStore('modelParameter', () => { speciesGroups.value = speciesGroups.value.map((group) => ({ ...group, - minimumDBHLimit: DEFAULT_VALUES.MINIMUM_DBH_LIMIT, })) becZone.value = DEFAULT_VALUES.BEC_ZONE siteSpeciesValues.value = DEFAULT_VALUES.SITE_SPECIES_VALUES - ageType.value = DEFAULT_VALUES.AGE_TYPE - percentStockableArea.value = DEFAULT_VALUES.PERCENT_STOCKABLE_AREA - age.value = DEFAULT_VALUES.AGE - height.value = DEFAULT_VALUES.HEIGHT bha50SiteIndex.value = DEFAULT_VALUES.BHA50_SITE_INDEX - floating.value = FLOATING.SITEINDEX - minimumDBHLimit.value = DEFAULT_VALUES.MINIMUM_DBH_LIMIT - currentDiameter.value = DEFAULT_VALUES.CURRENT_DIAMETER - percentCrownClosure.value = DEFAULT_VALUES.PERCENT_CROWN_CLOSURE - computedValues.value = DEFAULT_VALUES.COMPUTED_VALUES - loreyHeight.value = DEFAULT_VALUES.LOREY_HEIGHT - wholeStemVol75.value = DEFAULT_VALUES.WHOLE_STEM_VOL75 - basalArea125.value = DEFAULT_VALUES.BASAL_AREA125 - wholeStemVol125.value = DEFAULT_VALUES.WHOLE_STEM_VOL125 - cuVol.value = DEFAULT_VALUES.CU_VOL - cuNetDecayVol.value = DEFAULT_VALUES.CU_NET_DECAY_VOL - cuNetDecayWasteVol.value = DEFAULT_VALUES.CU_NET_DECAY_WASTE_VOL + percentStockableArea.value = DEFAULT_VALUES.PERCENT_STOCKABLE_AREA startingAge.value = DEFAULT_VALUES.STARTING_AGE finishingAge.value = DEFAULT_VALUES.FINISHING_AGE ageIncrement.value = DEFAULT_VALUES.AGE_INCREMENT @@ -280,29 +236,10 @@ export const useModelParameterStore = defineStore('modelParameter', () => { becZone, ecoZone, incSecondaryHeight, - siteIndexCurve, siteSpeciesValues, - ageType, - percentStockableArea, - age, - height, bha50SiteIndex, - floating, // stand density - basalArea, - treesPerHectare, - minimumDBHLimit, - percentCrownClosure, - currentDiameter, - // additional stand attributes - computedValues, - loreyHeight, - wholeStemVol75, - basalArea125, - wholeStemVol125, - cuVol, - cuNetDecayVol, - cuNetDecayWasteVol, + percentStockableArea, // report info startingAge, diff --git a/frontend/src/stores/projectionStore.ts b/frontend/src/stores/projectionStore.ts new file mode 100644 index 000000000..583c4ae8d --- /dev/null +++ b/frontend/src/stores/projectionStore.ts @@ -0,0 +1,53 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import * as JSZip from 'jszip' +import { messageResult } from '@/utils/messageHandler' +import { FILE_UPLOAD_ERR } from '@/constants/message' + +export const useProjectionStore = defineStore('projectionStore', () => { + const errorMessages = ref([]) + const logMessages = ref([]) + const yieldTable = ref('') + + const handleZipResponse = async (zipData: Blob) => { + try { + const zip = await JSZip.loadAsync(zipData) + const requiredFiles = { + error: 'Output_Error.txt', + log: 'Output_Log.txt', + yield: 'Output_YldTbl.csv', + } + + const errorFile = zip.file(requiredFiles.error) + const logFile = zip.file(requiredFiles.log) + const yieldFile = zip.file(requiredFiles.yield) + + if (!errorFile || !logFile || !yieldFile) { + const missingFiles = Object.values(requiredFiles).filter( + (file) => !zip.file(file), + ) + messageResult( + false, + '', + `${FILE_UPLOAD_ERR.MISSING_RESPONSED_FILE}: ${missingFiles.join(', ')}`, + ) + throw new Error(`Missing files: ${missingFiles.join(', ')}`) + } + + errorMessages.value = (await errorFile.async('string')).split(/\r?\n/) + logMessages.value = (await logFile.async('string')).split(/\r?\n/) + yieldTable.value = await yieldFile.async('string') + } catch (error) { + console.error('Error processing ZIP file:', error) + messageResult(false, '', FILE_UPLOAD_ERR.INVALID_RESPONSED_FILE) + throw error + } + } + + return { + errorMessages, + logMessages, + yieldTable, + handleZipResponse, + } +}) diff --git a/frontend/src/styles/style.scss b/frontend/src/styles/style.scss index c2ec1312b..507cdd2dd 100644 --- a/frontend/src/styles/style.scss +++ b/frontend/src/styles/style.scss @@ -105,6 +105,14 @@ body { padding: 1rem !important; } +/* FILE-INPUT */ +.v-input--center-affix .v-input__prepend, +.v-input--center-affix .v-input__append { + align-items: center; + padding-top: 0; + display: none; +} + /* TABS */ // tab .v-tab.v-tab.v-btn { diff --git a/frontend/src/types/types.ts b/frontend/src/types/types.ts index f3bbe91b6..b48778d92 100644 --- a/frontend/src/types/types.ts +++ b/frontend/src/types/types.ts @@ -16,7 +16,6 @@ export type PanelName = | typeof MODEL_PARAMETER_PANEL.SPECIES_INFO | typeof MODEL_PARAMETER_PANEL.SITE_INFO | typeof MODEL_PARAMETER_PANEL.STAND_DENSITY - | typeof MODEL_PARAMETER_PANEL.ADDT_STAND_ATTRS | typeof MODEL_PARAMETER_PANEL.REPORT_INFO export type PanelState = typeof PANEL.OPEN | typeof PANEL.CLOSE diff --git a/frontend/src/utils/messageHandler.ts b/frontend/src/utils/messageHandler.ts index ab81423ad..797974fc8 100644 --- a/frontend/src/utils/messageHandler.ts +++ b/frontend/src/utils/messageHandler.ts @@ -1,6 +1,7 @@ import { useNotificationStore } from '@/stores/common/notificationStore' import type { MessageType } from '@/types/types' import { MESSAGE_TYPE } from '@/constants/constants' +import { getActivePinia } from 'pinia' /** * Displays job success or failure messages to notification and/or console. @@ -15,14 +16,25 @@ export const messageResult = ( failMessage: string, error: Error | null = null, ) => { - const notificationStore = useNotificationStore() + const pinia = getActivePinia() + let notificationStore + + if (pinia) { + notificationStore = useNotificationStore() + } else { + console.warn('Pinia is not active. Message will only be logged.') + } if (isSuccess) { console.info(successMessage) - notificationStore.showSuccessMessage(successMessage) + if (notificationStore) { + notificationStore.showSuccessMessage(successMessage) + } } else { console.warn(failMessage, error) - notificationStore.showWarningMessage(failMessage) + if (notificationStore) { + notificationStore.showWarningMessage(failMessage) + } } } @@ -34,14 +46,21 @@ export const messageResult = ( * @param {boolean} [disableConsole=false] - Whether to disable console logging. * @param {boolean} [disableNotification=false] - Whether to disable notification messages. */ -export const logMessage = ( +const logMessage = ( message: string, messageType: MessageType = MESSAGE_TYPE.INFO, optionalMessage?: string | null, disableConsole: boolean = false, disableNotification: boolean = false, ) => { - const notificationStore = useNotificationStore() + const pinia = getActivePinia() + let notificationStore + + if (pinia) { + notificationStore = useNotificationStore() + } else { + console.warn('Pinia is not active. Message will only be logged.') + } const consoleMessage = optionalMessage ? `${message} (${optionalMessage})` @@ -69,19 +88,25 @@ export const logMessage = ( if (!disableNotification) { switch (messageType) { case MESSAGE_TYPE.ERROR: - notificationStore.showErrorMessage(message) + if (notificationStore) { + notificationStore.showErrorMessage(message) + } break case MESSAGE_TYPE.WARNING: - notificationStore.showWarningMessage(message) - break - case MESSAGE_TYPE.INFO: - notificationStore.showInfoMessage(message) + if (notificationStore) { + notificationStore.showWarningMessage(message) + } break case MESSAGE_TYPE.SUCCESS: - notificationStore.showSuccessMessage(message) + if (notificationStore) { + notificationStore.showSuccessMessage(message) + } break + case MESSAGE_TYPE.INFO: default: - notificationStore.showInfoMessage(message) + if (notificationStore) { + notificationStore.showInfoMessage(message) + } } } } diff --git a/frontend/src/validation/addtStandAttrsValidation.ts b/frontend/src/validation/addtStandAttrsValidation.ts deleted file mode 100644 index d3c8cc157..000000000 --- a/frontend/src/validation/addtStandAttrsValidation.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { ValidationBase } from './validationBase' -import { COMPUTED_VALUES, NUM_INPUT_LIMITS } from '@/constants/constants' -import { Util } from '@/utils/util' -import { MDL_PRM_INPUT_ERR } from '@/constants/message' - -export class AddtStandAttrsValidation extends ValidationBase { - validateFieldPresence(fieldValue: string | null): boolean { - return !Util.isBlank(fieldValue) - } - - validateAllFields(fields: { value: string | null }[]): boolean { - return fields.every((field) => this.validateFieldPresence(field.value)) - } - - validateComputedValuesModification( - computedValues: string | null, - fields: { original: { value: string | null }; current: string | null }[], - ): boolean { - if (computedValues === COMPUTED_VALUES.MODIFY) { - const hasModification = fields.some((field) => { - return field.original.value !== field.current - }) - - return hasModification - } - return true - } - - validateComparison( - basalArea125: string | null, - basalArea: string | null, - wholeStemVol125: string | null, - wholeStemVol75: string | null, - cuVol: string | null, - cuNetDecayVol: string | null, - cuNetDecayWasteVol: string | null, - ): string | null { - if ( - basalArea125 !== null && - basalArea !== null && - parseFloat(basalArea125) > parseFloat(basalArea) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_COMP_BSL_AREA(basalArea) - } - - if ( - wholeStemVol125 !== null && - wholeStemVol75 !== null && - wholeStemVol125 > wholeStemVol75 - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_COMP_WSV - } - - if (cuVol !== null && wholeStemVol125 !== null && cuVol > wholeStemVol125) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_COMP_CUV - } - - if (cuNetDecayVol !== null && cuVol !== null && cuNetDecayVol > cuVol) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_COMP_CUNDV - } - - if ( - cuNetDecayWasteVol !== null && - cuNetDecayVol !== null && - cuNetDecayWasteVol > cuNetDecayVol - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_COMP_CUNDWV - } - - return null - } - - validateValueRange( - loreyHeight: string | null, - wholeStemVol75: string | null, - basalArea125: string | null, - wholeStemVol125: string | null, - cuVol: string | null, - cuNetDecayVol: string | null, - cuNetDecayWasteVol: string | null, - ): string | null { - if ( - loreyHeight !== null && - (parseFloat(loreyHeight) < NUM_INPUT_LIMITS.LOREY_HEIGHT_MIN || - parseFloat(loreyHeight) > NUM_INPUT_LIMITS.LOREY_HEIGHT_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_LRY_HEIGHT_RNG - } - - if ( - wholeStemVol75 !== null && - (parseFloat(wholeStemVol75) < NUM_INPUT_LIMITS.WHOLE_STEM_VOL75_MIN || - parseFloat(wholeStemVol75) > NUM_INPUT_LIMITS.WHOLE_STEM_VOL75_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_WSV75_RNG - } - - if ( - basalArea125 !== null && - (parseFloat(basalArea125) < NUM_INPUT_LIMITS.BASAL_AREA125_MIN || - parseFloat(basalArea125) > NUM_INPUT_LIMITS.BASAL_AREA125_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_BSL_AREA_RNG - } - - if ( - wholeStemVol125 !== null && - (parseFloat(wholeStemVol125) < NUM_INPUT_LIMITS.WHOLE_STEM_VOL125_MIN || - parseFloat(wholeStemVol125) > NUM_INPUT_LIMITS.WHOLE_STEM_VOL125_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_WSV125_RNG - } - - if ( - cuVol !== null && - (parseFloat(cuVol) < NUM_INPUT_LIMITS.CU_VOL_MIN || - parseFloat(cuVol) > NUM_INPUT_LIMITS.CU_VOL_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_CUV_RNG - } - - if ( - cuNetDecayVol !== null && - (parseFloat(cuNetDecayVol) < NUM_INPUT_LIMITS.CU_NET_DECAY_VOL_MIN || - parseFloat(cuNetDecayVol) > NUM_INPUT_LIMITS.CU_NET_DECAY_VOL_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_CUNDV_RNG - } - - if ( - cuNetDecayWasteVol !== null && - (parseFloat(cuNetDecayWasteVol) < - NUM_INPUT_LIMITS.CU_NET_DECAY_WASTE_VOL_MIN || - parseFloat(cuNetDecayWasteVol) > - NUM_INPUT_LIMITS.CU_NET_DECAY_WASTE_VOL_MAX) - ) { - return MDL_PRM_INPUT_ERR.ATTR_VLD_CUNDWV_RNG - } - - return null - } -} diff --git a/frontend/src/validation/fileUploadValidation.ts b/frontend/src/validation/fileUploadValidation.ts new file mode 100644 index 000000000..c8c934cbe --- /dev/null +++ b/frontend/src/validation/fileUploadValidation.ts @@ -0,0 +1,66 @@ +import { ValidationBase } from './validationBase' +import { NUM_INPUT_LIMITS } from '@/constants/constants' +import Papa from 'papaparse' + +export class FileUploadValidation extends ValidationBase { + validateRequiredFields( + startingAge: number | null, + finishingAge: number | null, + ageIncrement: number | null, + ): boolean { + return ( + startingAge !== null && finishingAge !== null && ageIncrement !== null + ) + } + + validateAgeComparison( + finishingAge: number | null, + startingAge: number | null, + ): boolean { + if (finishingAge !== null && startingAge !== null) { + return finishingAge >= startingAge + } + return true + } + + validateStartingAgeRange(startingAge: number | null): boolean { + if (startingAge !== null) { + return ( + startingAge >= NUM_INPUT_LIMITS.STARTING_AGE_MIN && + startingAge <= NUM_INPUT_LIMITS.STARTING_AGE_MAX + ) + } + return true + } + + validateFinishingAgeRange(finishingAge: number | null): boolean { + if (finishingAge !== null) { + return ( + finishingAge >= NUM_INPUT_LIMITS.FINISHING_AGE_MIN && + finishingAge <= NUM_INPUT_LIMITS.FINISHING_AGE_MAX + ) + } + return true + } + + validateAgeIncrementRange(ageIncrement: number | null): boolean { + if (ageIncrement !== null) { + return ( + ageIncrement >= NUM_INPUT_LIMITS.AGE_INC_MIN && + ageIncrement <= NUM_INPUT_LIMITS.AGE_INC_MAX + ) + } + return true + } + + async isCSVFile(file: File): Promise { + return new Promise((resolve) => { + Papa.parse(file, { + complete: (results: any) => { + resolve(results.errors.length === 0) + }, + error: () => resolve(false), + }) + }) + } +} diff --git a/frontend/src/validation/siteInfoValidation.ts b/frontend/src/validation/siteInfoValidation.ts index 1030d5882..94c15a028 100644 --- a/frontend/src/validation/siteInfoValidation.ts +++ b/frontend/src/validation/siteInfoValidation.ts @@ -1,56 +1,10 @@ import { ValidationBase } from './validationBase' -import { SITE_SPECIES_VALUES, NUM_INPUT_LIMITS } from '@/constants/constants' +import { NUM_INPUT_LIMITS } from '@/constants/constants' import { Util } from '@/utils/util' export class SiteInfoValidation extends ValidationBase { - validateRequiredFields( - siteSpeciesValues: string | null, - age: number | null, - height: string | null, - bha50SiteIndex: string | null, - ): boolean { - if (siteSpeciesValues === SITE_SPECIES_VALUES.COMPUTED) { - return !( - Util.isEmptyOrZero(age) || - Util.isEmptyOrZero(height) || - Util.isEmptyOrZero(bha50SiteIndex) - ) - } else if (siteSpeciesValues === SITE_SPECIES_VALUES.SUPPLIED) { - return !Util.isEmptyOrZero(bha50SiteIndex) - } - return true - } - - validatePercentStockableAreaRange(psa: number | null): boolean { - if (!psa) return true - - return this.validateRange( - psa, - NUM_INPUT_LIMITS.PERCENT_STOCKABLE_AREA_MIN, - NUM_INPUT_LIMITS.PERCENT_STOCKABLE_AREA_MAX, - ) - } - - validateAgeRange(age: number | null): boolean { - if (!age) return true - - return this.validateRange( - age, - NUM_INPUT_LIMITS.AGE_MIN, - NUM_INPUT_LIMITS.AGE_MAX, - ) - } - - validateHeightRange(height: string | null): boolean { - if (!height) return true - - const numericHeight = parseFloat(height) - - return this.validateRange( - numericHeight, - NUM_INPUT_LIMITS.HEIGHT_MIN, - NUM_INPUT_LIMITS.HEIGHT_MAX, - ) + validateRequiredFields(bha50SiteIndex: string | null): boolean { + return !Util.isEmptyOrZero(bha50SiteIndex) } validateBha50SiteIndexRange(bha50SiteIndex: string | null): boolean { diff --git a/frontend/src/validation/standDensityValidation.ts b/frontend/src/validation/standDensityValidation.ts index 514ce0fc3..20443fe6c 100644 --- a/frontend/src/validation/standDensityValidation.ts +++ b/frontend/src/validation/standDensityValidation.ts @@ -1,363 +1,14 @@ import { ValidationBase } from './validationBase' import { NUM_INPUT_LIMITS } from '@/constants/constants' -import { Util } from '@/utils/util' -import * as MAP from '@/constants/mappings' export class StandDensityValidation extends ValidationBase { - validateBasalAreaRange(basalArea: string | null): boolean { - const numericBasalArea = Util.toNumber(basalArea) - if (!numericBasalArea) return true + validatePercentStockableAreaRange(psa: number | null): boolean { + if (!psa) return true return this.validateRange( - numericBasalArea, - NUM_INPUT_LIMITS.BASAL_AREA_MIN, - NUM_INPUT_LIMITS.BASAL_AREA_MAX, + psa, + NUM_INPUT_LIMITS.PERCENT_STOCKABLE_AREA_MIN, + NUM_INPUT_LIMITS.PERCENT_STOCKABLE_AREA_MAX, ) } - - validateTreesPerHectareRange(tph: string | null): boolean { - const numericTph = Util.toNumber(tph) - if (!numericTph) return true - - return this.validateRange( - numericTph, - NUM_INPUT_LIMITS.TPH_MIN, - NUM_INPUT_LIMITS.TPH_MAX, - ) - } - - validatePercentCrownClosureRange(pcc: number | null): boolean { - if (!pcc) return true - - return this.validateRange( - pcc, - NUM_INPUT_LIMITS.CROWN_CLOSURE_MIN, - NUM_INPUT_LIMITS.CROWN_CLOSURE_MAX, - ) - } - - validateBALimits( - selectedSiteSpecies: string | null, - becZone: string | null, - basalArea: string | null, - height: string | null, - ): boolean { - if (selectedSiteSpecies && becZone && basalArea && height) { - const isValid = this.validateBasalAreaLimits( - selectedSiteSpecies, - this.isCoastalZone(becZone), - basalArea, - height, - ) - - return isValid - } - return true - } - - validateTPHLimits( - basalArea: string | null, - treesPerHectare: string | null, - height: string | null, - selectedSiteSpecies: string | null, - becZone: string | null, - ): string | null { - if ( - basalArea && - treesPerHectare && - height && - selectedSiteSpecies && - becZone - ) { - return this.validateTreePerHectareLimits( - basalArea, - treesPerHectare, - height, - selectedSiteSpecies, - this.isCoastalZone(becZone), - ) - } - - return null - } - - validateQuadDiameter( - basalArea: string | null, - treesPerHectare: string | null, - minimumDBHLimit: string | null, - ): string | null { - if (basalArea && treesPerHectare && minimumDBHLimit) { - return this.validateQuadraticDiameter( - basalArea, - treesPerHectare, - minimumDBHLimit, - ) - } - - return null - } - - /** - * Function that validates whether the given basal area is within acceptable limits for the species. - * It calculates the maximum allowable basal area using species-specific coefficients and height. - * @param species - The species code to look up the basal area coefficients. - * @param isCoastal - Boolean indicating whether the region is coastal (true) or interior (false). - * @param basalArea - The basal area to be validated. - * @param height - The height of the stand used in the calculation. - * @returns true if the basal area is within the valid limit, false otherwise. - * Returns true on errors for input arguments that cannot be validated. - * @example speceis:'H', coastal, ba: 50, height: 8 - */ - validateBasalAreaLimits( - species: string, - isCoastal: boolean, - basalArea: string, - height: string, - ): boolean { - if (!(species in MAP.BA_LIMIT_COEFFICIENTS)) { - console.warn(`Species ${species} not found in BA_LIMIT_COEFFICIENTS.`) - return true - } - - const speciesData = - MAP.BA_LIMIT_COEFFICIENTS[ - species as keyof typeof MAP.BA_LIMIT_COEFFICIENTS - ] - - const region = isCoastal ? 'coastal' : 'interior' - const coeffs = speciesData[region] - - // -999 indicates unavailable coefficient - if (coeffs.coeff1 === -999 || coeffs.coeff2 === -999) { - return true - } - - // Ensure these arguments are valid, handle invalid input - const parsedHeight = parseFloat(height) - const parsedBasalArea = parseFloat(basalArea) - - if (isNaN(parsedHeight) || parsedHeight <= 0) { - console.warn( - `Invalid height value: ${height}. Unable to perform calculation.`, - ) - return true - } - - if (isNaN(parsedBasalArea) || parsedBasalArea <= 0) { - console.warn( - `Invalid basal area value: ${basalArea}. Unable to perform calculation.`, - ) - return true - } - - // Equation constants - const { const1, const2 } = MAP.BA_EQUATION_CONSTANTS - - // Validate the basal area against the calculated limit - const fBALimit = - Math.exp(coeffs.coeff2 / (parseFloat(height) - const2)) * coeffs.coeff1 + - const1 - - return parseFloat(basalArea) <= fBALimit - } - - /** - * Function that takes in a BEC Zone and returns whether the Zone is coastal or not - * @param becZone BEC Zone code - * @returns true if the BEC Zone is coastal, false if interior - */ - isCoastalZone(becZone: string): boolean { - const normalizedBecZone = becZone.trim().toUpperCase() - - if (normalizedBecZone in MAP.BEC_ZONE_COASTAL_MAP) { - return MAP.BEC_ZONE_COASTAL_MAP[normalizedBecZone] - } else { - console.warn(`BEC Zone ${becZone} is not recognized.`) - return false - } - } - - /** - * Validates the Trees per Hectare (TPH) limits based on the basal area, TPH, height, species, and region. - * This function calculates the likely minimum and maximum TPH values using species-specific coefficients - * for coastal or interior regions and compares the input TPH with those limits. - * - * @param basalArea - The basal area in square meters per hectare. - * @param tph - Trees per hectare. - * @param height - The height of the trees in meters. - * @param species - The species code to look up TPH limit coefficients. - * @param coastal - Boolean indicating whether the region is coastal (true) or interior (false). - * @returns A string with an error message if TPH is outside the calculated limits, or null if valid. - * - * @example - * tph < tphMin: species = AC, coastal, height = 10.0, ba = 25.0, TPH = 50 - * tph > tphMax: species = AC, coastal, height = 10.0, ba = 5.0, TPH = 4000 - */ - validateTreePerHectareLimits( - basalArea: string, - tph: string, - height: string, - species: string, - coastal: boolean, - ): string | null { - // Handle unknown species - if (!(species in MAP.TPH_LIMIT_COEFFICIENTS)) { - console.warn('Unknown species') - return null - } - - const speciesCoefficients = MAP.TPH_LIMIT_COEFFICIENTS[ - species as keyof typeof MAP.TPH_LIMIT_COEFFICIENTS - ] as { coastal?: any; interior?: any } - - // Set region based on whether it's Coastal or not - const region = coastal ? 'coastal' : 'interior' - - // Check if the region data exists - if (!speciesCoefficients[region]) { - console.warn('No data for region') - return null - } - - const regionCoefficients = speciesCoefficients[region] - const { P10, P90 } = regionCoefficients || {} - - // Verify that P10 and P90 data are present - if (!P10 || !P90) { - console.warn('No data for region') - return null - } - - // Ensure these input arguments are valid, handle invalid input - const parsedHeight = parseFloat(height) - const parsedBasalArea = parseFloat(basalArea) - const parsedTph = parseFloat(tph) - - if (isNaN(parsedHeight) || parsedHeight <= 0) { - console.warn( - `Invalid height value: ${height}. Unable to perform calculation.`, - ) - return null - } - - if (isNaN(parsedBasalArea) || parsedBasalArea <= 0) { - console.warn( - `Invalid basal area value: ${basalArea}. Unable to perform calculation.`, - ) - return null - } - - if (isNaN(parsedTph) || parsedTph <= 0) { - console.warn( - `Invalid trees per hectare value: ${tph}. Unable to perform calculation.`, - ) - return null - } - - // Defining constants - const const1 = MAP.TPH_EQUATION_CONSTANTS.const1 - const const2 = MAP.TPH_EQUATION_CONSTANTS.const2 - const const3 = MAP.TPH_EQUATION_CONSTANTS.const3 - const unavailable = -999 // Handle when data is unavailable - - let tphMin: number | null = null - let tphMax: number | null = null - - // Return null if P10, P90 values are invalid - if (P10.a0 === unavailable || P90.a0 === unavailable) { - console.warn('Unavailable TPH coefficients for this region') - return null - } - - // Minimum TPH calculation - const dqMax = - const1 + - P90.a0 + - P90.b0 * (parseFloat(height) - const2) + - P90.b1 * (parseFloat(height) - const2) ** 2 - - if (dqMax > 0) { - tphMin = parseFloat(basalArea) / (const3 * dqMax ** 2) - } - - // Maximum TPH calculation - const dqMin = - -const1 + - P10.a0 + - P10.b0 * (parseFloat(height) - const2) + - P10.b1 * (parseFloat(height) - const2) ** 2 - - if (dqMin > 0) { - tphMax = parseFloat(basalArea) / (const3 * dqMin ** 2) - } - - // Minimum and maximum TPH values not calculated correctly - if (tphMin === null || tphMax === null) { - console.warn('TPH calculation failed') - return null - } - - if (parseFloat(tph) < tphMin) { - return 'Trees/ha is less than a likely minimum for entered height. Do you wish to proceed?' - } - - if (parseFloat(tph) > tphMax) { - return 'Trees/ha is above a likely maximum for entered height. Do you wish to proceed?' - } - - // Return null if in range (valid value) - return null - } - - /** - * Validates the quadratic mean diameter based on the basal area, trees per hectare (TPH), and minimum DBH limit. - * If the calculated quadratic diameter is less than the required minimum DBH limit, an error message is returned. - * - * @param basalArea - The basal area in m²/ha. - * @param tph - Trees per hectare. - * @param minDBHLimit - The minimum DBH limit in cm, provided as a string. - * @returns - Returns an error message if the quadratic diameter is less than the minimum limit, otherwise null. - * - * @example - * basalArea: 4, tph: 1000, minDBHLimit: 7.5 cm+ - */ - validateQuadraticDiameter( - basalArea: string, - tph: string, - minDBHLimit: string | null, - ): string | null { - if (!minDBHLimit) { - console.warn('Unknown minDBHLimit') - return null - } - - const parsedBasalArea = parseFloat(basalArea) - const parsedTph = parseFloat(tph) - - if (isNaN(parsedBasalArea) || parsedBasalArea <= 0) { - console.warn( - `Invalid basal area value: ${basalArea}. Unable to perform calculation.`, - ) - return null - } - - if (isNaN(parsedTph) || parsedTph <= 0) { - console.warn( - `Invalid trees per hectare value: ${tph}. Unable to perform calculation.`, - ) - return null - } - - let diam = 0 - if (parseFloat(tph) > 0) { - diam = Math.sqrt(parseFloat(basalArea) / parseFloat(tph) / 0.00007854) - } - - if (diam < Util.extractNumeric(minDBHLimit)) { - return `Quadratic Mean Diameter of ${diam.toFixed(1)} cm is less than the required diameter of: ${minDBHLimit}\nModify one or both of Basal Area or Trees per Hectare.` - } - - // Return null if in range (valid value) - return null - } } diff --git a/frontend/src/views/input-model-parameters/FileUpload.vue b/frontend/src/views/input-model-parameters/FileUpload.vue index 847f681df..4463a45d0 100644 --- a/frontend/src/views/input-model-parameters/FileUpload.vue +++ b/frontend/src/views/input-model-parameters/FileUpload.vue @@ -1,8 +1,8 @@ @@ -79,7 +82,6 @@ import ViewErrorMessages from '@/views/input-model-parameters/ViewErrorMessages. import SiteInfo from '@/components/model-param-selection-panes/SiteInfo.vue' import StandDensity from '@/components/model-param-selection-panes/StandDensity.vue' -import AddtStandAttrs from '@/components/model-param-selection-panes/AddtStandAttrs.vue' import ReportInfo from '@/components/model-param-selection-panes/ReportInfo.vue' import FileUpload from '@/views/input-model-parameters/FileUpload.vue' @@ -89,35 +91,26 @@ import { MODEL_SELECTION, MODEL_PARAM_TAB_NAME } from '@/constants/constants' const appStore = useAppStore() const modelParameterStore = useModelParameterStore() -const { currentTab, modelType } = storeToRefs(appStore) - -const tabs = computed(() => { - if (modelType.value === MODEL_SELECTION.FILE_UPLOAD) { - return [ - { - label: MODEL_PARAM_TAB_NAME.FILE_UPLOAD, - component: FileUpload, - }, - { label: MODEL_PARAM_TAB_NAME.MODEL_REPORT, component: ModelReport }, - { label: MODEL_PARAM_TAB_NAME.VIEW_LOG_FILE, component: ViewLogFile }, - { - label: MODEL_PARAM_TAB_NAME.VIEW_ERROR_MESSAGES, - component: ViewErrorMessages, - }, - ] - } - return [ - { - label: MODEL_PARAM_TAB_NAME.MODEL_PARAM_SELECTION, - component: ModelParameterSelection, - }, - { label: MODEL_PARAM_TAB_NAME.MODEL_REPORT, component: ModelReport }, - { label: MODEL_PARAM_TAB_NAME.VIEW_LOG_FILE, component: ViewLogFile }, - { - label: MODEL_PARAM_TAB_NAME.VIEW_ERROR_MESSAGES, - component: ViewErrorMessages, - }, - ] +const { currentTab, modelSelection } = storeToRefs(appStore) + +const tabs = [ + { + label: MODEL_PARAM_TAB_NAME.MODEL_PARAM_SELECTION, + component: ModelParameterSelection, + }, + { label: MODEL_PARAM_TAB_NAME.MODEL_REPORT, component: ModelReport }, + { label: MODEL_PARAM_TAB_NAME.VIEW_LOG_FILE, component: ViewLogFile }, + { + label: MODEL_PARAM_TAB_NAME.VIEW_ERROR_MESSAGES, + component: ViewErrorMessages, + }, +] + +const isModelParameterPanelsVisible = computed(() => { + return ( + modelSelection.value === MODEL_SELECTION.INPUT_MODEL_PARAMETERS && + currentTab.value === 0 + ) }) onMounted(() => { @@ -127,7 +120,7 @@ onMounted(() => { const runModel = () => {} - diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index e2d5ac4d0..bc71fcdf0 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,39 +1,52 @@ import { fileURLToPath, URL } from 'node:url' -import { defineConfig } from 'vite' +import { defineConfig, loadEnv } from 'vite' import Vue from '@vitejs/plugin-vue' import Vuetify from 'vite-plugin-vuetify' import packageVersion from 'vite-plugin-package-version' // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - { - name: 'build-html', - apply: 'build', - transformIndexHtml: (html) => { - return { - html, - tags: [ - { - tag: 'script', - attrs: { - src: '/env.js', +export default defineConfig(({ mode }) => { + process.env = { ...process.env, ...loadEnv(mode, process.cwd()) } + return { + plugins: [ + { + name: 'build-html', + apply: 'build', + transformIndexHtml: (html) => { + return { + html, + tags: [ + { + tag: 'script', + attrs: { + src: '/env.js', + }, + injectTo: 'head', }, - injectTo: 'head', - }, - ], - } + ], + } + }, + }, + Vue(), + packageVersion(), + Vuetify({ + autoImport: true, + }), + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, - Vue(), - packageVersion(), - Vuetify({ - autoImport: true, - }), - ], - resolve: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)), + server: { + proxy: { + // Proxy API requests to the backend + '/api': { + target: process.env.VITE_API_URL, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + }, + }, }, - }, + } })