diff --git a/housekeeping-scripts/s3-images-resizing/.env.example b/housekeeping-scripts/s3-images-resizing/.env.example new file mode 100644 index 000000000..122b5224b --- /dev/null +++ b/housekeeping-scripts/s3-images-resizing/.env.example @@ -0,0 +1,9 @@ +## S3 +AWS_S3_REGION: ap-south-1 +AWS_S3_BUCKET: +AWS_ACCESS_KEY_ID: +AWS_SECRET_ACCESS_KEY: + +## CSV +CSV_INPUT_PATH: sample.csv +CSV_OUTPUT_PATH: \ No newline at end of file diff --git a/housekeeping-scripts/s3-images-resizing/.gitignore b/housekeeping-scripts/s3-images-resizing/.gitignore new file mode 100644 index 000000000..2de29a58d --- /dev/null +++ b/housekeeping-scripts/s3-images-resizing/.gitignore @@ -0,0 +1,39 @@ +# 3pd +*/bower_components/* +*/jspm_packages/* +*/node_modules/* + +# Cache files +.cache +.eslintcache +.parcel-cache +.npm +.yarn/cache +.yarn/unplugged +.yarn/install-state.gz + +# Dist / builds +*/build/* +*/builds/* +*/dist/* + +# G cloud config +educate-girls-*.json + +# Env files +.env +.env.* + +# Log files +lerna-debug.log +npm-debug.log +yarn-error.log +yarn-debug.log +.serverless + +# OS files +.DS_Store + +# VS editor-related files +.vscode +.vscode-test diff --git a/housekeeping-scripts/s3-images-resizing/index.js b/housekeeping-scripts/s3-images-resizing/index.js new file mode 100644 index 000000000..c47565b89 --- /dev/null +++ b/housekeeping-scripts/s3-images-resizing/index.js @@ -0,0 +1,234 @@ +const { S3Client, ListObjectsV2Command, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3'); +const fs = require('fs'); +const path = require('path'); +const { parse } = require('csv-parse'); +const sharp = require('sharp'); +const util = require('util'); +require('dotenv').config() + +// Logger +const log_file = fs.createWriteStream(__dirname + '/debug-' + new Date().toISOString().replace(/T/, '_').replace(/\..+/, '') + '.log', { flags: 'w' }); +const log_stdout = process.stdout; +const add_log = function (d) { // + log_file.write(util.format(d) + '\n'); + log_stdout.write(util.format(d) + '\n'); +}; + +// CSV paths +/*SELECT id, name, provider, path, doument_type +FROM documents +WHERE doument_type IN ('profile', 'profile_photo', 'profile_photo_1', 'profile_photo_2', 'profile_photo_3') +AND provider = 's3' +AND name <> 'NULL' +ORDER BY id*/ +const CSV_INPUT_PATH = path.join(__dirname, process.env.CSV_INPUT_PATH); +let images_from_csv = []; + +// Configure AWS credentials (using environment variables or other preferred methods) +const BUCKET_NAME = `${process.env.AWS_S3_BUCKET}`; +const s3Client = new S3Client({ + region: `${process.env.AWS_S3_REGION}`, + credentials: { + accessKeyId: `${process.env.AWS_ACCESS_KEY_ID}`, + secretAccessKey: `${process.env.AWS_SECRET_ACCESS_KEY}`, + }, +}); + +// Optional prefix to filter images +const PREFIX = ''; + +// Resize to multiple widths in pixels +const imageResizeOptions = [32, 64, 128, 256]; + +// Function to check file mime type (no change) +async function getImagesFromCsv() { + add_log("START: Reading CSV"); + + fs.createReadStream(CSV_INPUT_PATH) + .pipe(parse({ delimiter: ",", from_line: 2 })) + .on("data", function (row) { + images_from_csv[images_from_csv.length] = row[1]; + }) + .on("end", function () { + add_log("END: Reading CSV"); + }) + .on("error", function (error) { + add_log("ERROR: Reading CSV: " + error.message); + }); +} + +// Get list of objects from S3 +async function getObjectsFromS3() { + let s3Files = []; + let continuationToken; + + const listObjectsV2CommandParams = { + Bucket: BUCKET_NAME, + Prefix: PREFIX, + MaxKeys: 1000, + }; + + // 2.1 Paginate through all objects in the bucket + add_log("START: Get objects list from S3"); + + let s3_get_objects_list_loop_counter = 0; + do { + s3_get_objects_list_loop_counter++; + add_log("INFO: Getting paginated objects list from S3, loop #" + s3_get_objects_list_loop_counter); + + listObjectsV2CommandParams.ContinuationToken = continuationToken; + let listObjectsCommand = new ListObjectsV2Command(listObjectsV2CommandParams); + let listObjectsResult = await s3Client.send(listObjectsCommand); + s3Files = s3Files.concat(listObjectsResult.Contents); + continuationToken = listObjectsResult.NextContinuationToken; + } while (continuationToken); + + add_log("END: Get objects list from S3"); + + return s3Files; +} + +// Function to check file mime type (no change) +async function isImage(object) { + const ext = path.extname(object.Key).toLowerCase(); + const imageExtensions = ['.gif', '.jpg', '.jpeg', '.png', '.webp']; + + return imageExtensions.includes(ext); +} + +// Function to download and resize image (updated for SDK v3) +async function resizeImage(object) { + // 1 - Get image from S3 + const getObjectCommandResponse = new GetObjectCommand({ Bucket: BUCKET_NAME, Key: object.Key }); + const data = await s3Client.send(getObjectCommandResponse); + + // 2 - Write image from S3 to temporary file + fs.writeFileSync(`temp-${object.Key}`, await data.Body.transformToByteArray()); + const image = await sharp(`temp-${object.Key}`); + + // 3 - Resize temp images into different dimensions + for (const size of imageResizeOptions) { + // 3.1 - Set new path for resized image + let newKey = `resized/${size}x${size}/${object.Key}`; + + let width = size; // Specify the desired width + let height = size; // Specify the desired height + let options = { + fit: sharp.fit.contain, // Specify the fit strategy (cover, contain, fill, inside, outside) + // position: sharp.strategy.entropy, // Specify the position strategy for cover + }; + + // 3.2 - Resize image using sharp + await image + .keepExif() + .resize(width, height, options) + .toFile(newKey, async (err, info) => { + if (err) { + add_log(`ERROR: SHARP - Error while resizig image: ${err}`); + } else { + add_log(`INFO: SHARP - Resized image : ${newKey}`); + + // 3.3 - Read resized image + // Read the file content + let fileContentToBeUploaded = fs.readFileSync(newKey); + + // 3.4 - Upload resized image to S3 + const putObjectCommandParams = { + Bucket: BUCKET_NAME, + Key: newKey, + Body: fileContentToBeUploaded + }; + + add_log(`INFO: S3 - Uploading resized image: ${newKey}`); + await s3Client.send(new PutObjectCommand(putObjectCommandParams)); + // add_log(`s3upload response:`, JSON.stringify(response, null, 2)) + + // Delete the temporary file + await deleteTempFile(newKey); + } + }); + } + + // Delete the temporary file + await deleteTempFile(`temp-${object.Key}`); +} + +async function deleteTempFile(fileName) { + // Delete the temporary file + setTimeout(() => { + add_log(`INFO: Delete local temp image: ${fileName}`); + fs.unlinkSync(`${fileName}`); + }, 3000); +} + +// Process objects from S3 +async function processS3Objects(s3Files) { + // 1 - Process all objects + let s3ImagesCounter = 0; + let s3NonImagesCounter = 0; + + for (const object of s3Files) { + // 1.1 - Only process objects that are there in CSV + if (!images_from_csv.includes(object.Key)) { + s3NonImagesCounter++; + + add_log(`INFO: Skipping image #${s3NonImagesCounter} which is not in CSV with name: ${object.Key}`); + + continue; + } + + // 1.2 - Only process images and skip other files + if (await isImage(object)) { + s3ImagesCounter++; + add_log(`INFO: Processing image #${s3ImagesCounter} with name: ${object.Key}`); + + await resizeImage(object); + } else { + s3NonImagesCounter++; + + add_log(`INFO: Skipping non-image #${s3NonImagesCounter} with name: ${object.Key}`); + } + } + + return [s3ImagesCounter, s3NonImagesCounter]; +} + +// Main function (updated for SDK v3) +async function main() { + let start = +new Date(); + add_log('START: Executing script'); + + try { + // 1 - Get image names to be processed from CSV + await getImagesFromCsv(); + + // 2 - Get S3 objects from S3 + let s3Files = await getObjectsFromS3(); + + // 2.1 Print info + add_log(`=======================================`); + add_log(`Found ${s3Files.length} files in bucket ${BUCKET_NAME}`); + add_log(`Found ${images_from_csv.length} files in CSV`); + add_log(`=======================================`); + + // 3 - Process all objects + let processedCounters = await processS3Objects(s3Files); + + // 3.1 Print info + add_log(`=======================================`); + add_log(`Found ${s3Files.length} files`); + add_log(`Processed ${processedCounters[0]} images`); + add_log(`Skipped ${processedCounters[1]} files`); + add_log(`=======================================`); + + } catch (error) { + add_log('ERROR: Executing script: ' + error); + } + + let end = +new Date(); + add_log('END: Executing script'); + add_log("Time taken " + (end - start) + " milliseconds"); +} + +// Execute the script +main(); diff --git a/housekeeping-scripts/s3-images-resizing/package-lock.json b/housekeeping-scripts/s3-images-resizing/package-lock.json new file mode 100644 index 000000000..8f112e09a --- /dev/null +++ b/housekeeping-scripts/s3-images-resizing/package-lock.json @@ -0,0 +1,2692 @@ +{ + "name": "eg-scripting", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "eg-scripting", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.496.0", + "csv": "^6.3.6", + "dotenv": "^16.4.1", + "sharp": "^0.33.2" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/crc32c": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32c/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.496.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.496.0", + "@aws-sdk/middleware-bucket-endpoint": "3.496.0", + "@aws-sdk/middleware-expect-continue": "3.496.0", + "@aws-sdk/middleware-flexible-checksums": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-location-constraint": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-sdk-s3": "3.496.0", + "@aws-sdk/middleware-signing": "3.496.0", + "@aws-sdk/middleware-ssec": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/signature-v4-multi-region": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@aws-sdk/xml-builder": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/eventstream-serde-browser": "^2.1.1", + "@smithy/eventstream-serde-config-resolver": "^2.1.1", + "@smithy/eventstream-serde-node": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-blob-browser": "^2.1.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/hash-stream-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/md5-js": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-stream": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^1.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.496.0", + "@aws-sdk/credential-provider-process": "3.496.0", + "@aws-sdk/credential-provider-sso": "3.496.0", + "@aws-sdk/credential-provider-web-identity": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.496.0", + "@aws-sdk/credential-provider-ini": "3.496.0", + "@aws-sdk/credential-provider-process": "3.496.0", + "@aws-sdk/credential-provider-sso": "3.496.0", + "@aws-sdk/credential-provider-web-identity": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.496.0", + "@aws-sdk/token-providers": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/types": "3.496.0", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.495.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "@smithy/util-endpoints": "^1.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.495.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.496.0", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.2", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "glibc": ">=2.26", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.1" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.1", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "macos": ">=11", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0", + "yarn": ">=3.2.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "1.3.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "2.2.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.4.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^2.1.1", + "@smithy/chunked-blob-reader-native": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "2.4.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "8.3.2", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "2.2.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "2.3.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "3.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.3.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "2.3.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "2.9.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "2.2.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "2.2.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^2.1.1", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "1.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "2.1.1", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/bowser": { + "version": "2.11.0", + "license": "MIT" + }, + "node_modules/color": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/csv": { + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/csv/-/csv-6.3.6.tgz", + "integrity": "sha512-jsEsX2HhGp7xiwrJu5srQavKsh+HUJcCi78Ar3m4jlmFKRoTkkMy7ZZPP+LnQChmaztW+uj44oyfMb59daAs/Q==", + "dependencies": { + "csv-generate": "^4.3.1", + "csv-parse": "^5.5.3", + "csv-stringify": "^6.4.5", + "stream-transform": "^3.3.0" + }, + "engines": { + "node": ">= 0.1.90" + } + }, + "node_modules/csv-generate": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.3.1.tgz", + "integrity": "sha512-7YeeJq+44/I/O5N2sr2qBMcHZXhpfe38eh7DOFxyMtYO+Pir7kIfgFkW5MPksqKqqR6+/wX7UGoZm1Ot11151w==" + }, + "node_modules/csv-parse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.3.tgz", + "integrity": "sha512-v0KW6C0qlZzoGjk6u5tLmVfyZxNgPGXZsWTXshpAgKVGmGXzaVWGdlCFxNx5iuzcXT/oJN1HHM9DZKwtAtYa+A==" + }, + "node_modules/csv-stringify": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.5.tgz", + "integrity": "sha512-SPu1Vnh8U5EnzpNOi1NDBL5jU5Rx7DVHr15DNg9LXDTAbQlAVAmEbVt16wZvEW9Fu9Qt4Ji8kmeCJ2B1+4rFTQ==" + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.2", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "semver": "^7.5.4" + }, + "engines": { + "libvips": ">=8.15.1", + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.2", + "@img/sharp-darwin-x64": "0.33.2", + "@img/sharp-libvips-darwin-arm64": "1.0.1", + "@img/sharp-libvips-darwin-x64": "1.0.1", + "@img/sharp-libvips-linux-arm": "1.0.1", + "@img/sharp-libvips-linux-arm64": "1.0.1", + "@img/sharp-libvips-linux-s390x": "1.0.1", + "@img/sharp-libvips-linux-x64": "1.0.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.1", + "@img/sharp-libvips-linuxmusl-x64": "1.0.1", + "@img/sharp-linux-arm": "0.33.2", + "@img/sharp-linux-arm64": "0.33.2", + "@img/sharp-linux-s390x": "0.33.2", + "@img/sharp-linux-x64": "0.33.2", + "@img/sharp-linuxmusl-arm64": "0.33.2", + "@img/sharp-linuxmusl-x64": "0.33.2", + "@img/sharp-wasm32": "0.33.2", + "@img/sharp-win32-ia32": "0.33.2", + "@img/sharp-win32-x64": "0.33.2" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/stream-transform": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.0.tgz", + "integrity": "sha512-pG1NeDdmErNYKtvTpFayrEueAmL0xVU5wd22V7InGnatl4Ocq3HY7dcXIKj629kXvYQvglCC7CeDIGAlx1RNGA==" + }, + "node_modules/strnum": { + "version": "1.0.5", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.6.2", + "license": "0BSD" + }, + "node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + } + }, + "dependencies": { + "@aws-crypto/crc32": { + "version": "3.0.0", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/crc32c": { + "version": "3.0.0", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/ie11-detection": { + "version": "3.0.0", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/sha1-browser": { + "version": "3.0.0", + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/sha256-browser": { + "version": "3.0.0", + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/sha256-js": { + "version": "3.0.0", + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "requires": { + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-crypto/util": { + "version": "3.0.0", + "requires": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1" + } + } + }, + "@aws-sdk/client-s3": { + "version": "3.496.0", + "requires": { + "@aws-crypto/sha1-browser": "3.0.0", + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.496.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.496.0", + "@aws-sdk/middleware-bucket-endpoint": "3.496.0", + "@aws-sdk/middleware-expect-continue": "3.496.0", + "@aws-sdk/middleware-flexible-checksums": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-location-constraint": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-sdk-s3": "3.496.0", + "@aws-sdk/middleware-signing": "3.496.0", + "@aws-sdk/middleware-ssec": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/signature-v4-multi-region": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@aws-sdk/xml-builder": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/eventstream-serde-browser": "^2.1.1", + "@smithy/eventstream-serde-config-resolver": "^2.1.1", + "@smithy/eventstream-serde-node": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-blob-browser": "^2.1.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/hash-stream-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/md5-js": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-stream": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sso": { + "version": "3.496.0", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sts": { + "version": "3.496.0", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/core": { + "version": "3.496.0", + "requires": { + "@smithy/core": "^1.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.496.0", + "requires": { + "@aws-sdk/credential-provider-env": "3.496.0", + "@aws-sdk/credential-provider-process": "3.496.0", + "@aws-sdk/credential-provider-sso": "3.496.0", + "@aws-sdk/credential-provider-web-identity": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.496.0", + "requires": { + "@aws-sdk/credential-provider-env": "3.496.0", + "@aws-sdk/credential-provider-ini": "3.496.0", + "@aws-sdk/credential-provider-process": "3.496.0", + "@aws-sdk/credential-provider-sso": "3.496.0", + "@aws-sdk/credential-provider-web-identity": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.496.0", + "requires": { + "@aws-sdk/client-sso": "3.496.0", + "@aws-sdk/token-providers": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-bucket-endpoint": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-expect-continue": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-flexible-checksums": { + "version": "3.496.0", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@aws-crypto/crc32c": "3.0.0", + "@aws-sdk/types": "3.496.0", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-location-constraint": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-sdk-s3": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-arn-parser": "3.495.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-ssec": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/region-config-resolver": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/signature-v4-multi-region": { + "version": "3.496.0", + "requires": { + "@aws-sdk/middleware-sdk-s3": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/token-providers": { + "version": "3.496.0", + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/types": { + "version": "3.496.0", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-arn-parser": { + "version": "3.495.0", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "@smithy/util-endpoints": "^1.1.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.495.0", + "requires": { + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.496.0", + "requires": { + "@aws-sdk/types": "3.496.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "requires": { + "tslib": "^2.3.1" + } + }, + "@aws-sdk/xml-builder": { + "version": "3.496.0", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@img/sharp-darwin-arm64": { + "version": "0.33.2", + "optional": true, + "requires": { + "@img/sharp-libvips-darwin-arm64": "1.0.1" + } + }, + "@img/sharp-libvips-darwin-arm64": { + "version": "1.0.1", + "optional": true + }, + "@smithy/abort-controller": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/chunked-blob-reader": { + "version": "2.1.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/chunked-blob-reader-native": { + "version": "2.1.1", + "requires": { + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/config-resolver": { + "version": "2.1.1", + "requires": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/core": { + "version": "1.3.1", + "requires": { + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/credential-provider-imds": { + "version": "2.2.1", + "requires": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/eventstream-codec": { + "version": "2.1.1", + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/eventstream-serde-browser": { + "version": "2.1.1", + "requires": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/eventstream-serde-config-resolver": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/eventstream-serde-node": { + "version": "2.1.1", + "requires": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/eventstream-serde-universal": { + "version": "2.1.1", + "requires": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/fetch-http-handler": { + "version": "2.4.1", + "requires": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/hash-blob-browser": { + "version": "2.1.1", + "requires": { + "@smithy/chunked-blob-reader": "^2.1.1", + "@smithy/chunked-blob-reader-native": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/hash-node": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/hash-stream-node": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/invalid-dependency": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/is-array-buffer": { + "version": "2.1.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/md5-js": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/middleware-content-length": { + "version": "2.1.1", + "requires": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/middleware-endpoint": { + "version": "2.4.1", + "requires": { + "@smithy/middleware-serde": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/middleware-retry": { + "version": "2.1.1", + "requires": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2" + } + } + }, + "@smithy/middleware-serde": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/middleware-stack": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/node-config-provider": { + "version": "2.2.1", + "requires": { + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/node-http-handler": { + "version": "2.3.1", + "requires": { + "@smithy/abort-controller": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/property-provider": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/protocol-http": { + "version": "3.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/querystring-builder": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "@smithy/util-uri-escape": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/querystring-parser": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/service-error-classification": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1" + } + }, + "@smithy/shared-ini-file-loader": { + "version": "2.3.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/signature-v4": { + "version": "2.1.1", + "requires": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/smithy-client": { + "version": "2.3.1", + "requires": { + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/types": { + "version": "2.9.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/url-parser": { + "version": "2.1.1", + "requires": { + "@smithy/querystring-parser": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-base64": { + "version": "2.1.1", + "requires": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-body-length-browser": { + "version": "2.1.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/util-body-length-node": { + "version": "2.2.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/util-buffer-from": { + "version": "2.1.1", + "requires": { + "@smithy/is-array-buffer": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-config-provider": { + "version": "2.2.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/util-defaults-mode-browser": { + "version": "2.1.1", + "requires": { + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@smithy/util-defaults-mode-node": { + "version": "2.1.1", + "requires": { + "@smithy/config-resolver": "^2.1.1", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-endpoints": { + "version": "1.1.1", + "requires": { + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-hex-encoding": { + "version": "2.1.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/util-middleware": { + "version": "2.1.1", + "requires": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-retry": { + "version": "2.1.1", + "requires": { + "@smithy/service-error-classification": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-stream": { + "version": "2.1.1", + "requires": { + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-uri-escape": { + "version": "2.1.1", + "requires": { + "tslib": "^2.5.0" + } + }, + "@smithy/util-utf8": { + "version": "2.1.1", + "requires": { + "@smithy/util-buffer-from": "^2.1.1", + "tslib": "^2.5.0" + } + }, + "@smithy/util-waiter": { + "version": "2.1.1", + "requires": { + "@smithy/abort-controller": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + } + }, + "bowser": { + "version": "2.11.0" + }, + "color": { + "version": "4.2.3", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + } + }, + "color-convert": { + "version": "2.0.1", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4" + }, + "color-string": { + "version": "1.9.1", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "csv": { + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/csv/-/csv-6.3.6.tgz", + "integrity": "sha512-jsEsX2HhGp7xiwrJu5srQavKsh+HUJcCi78Ar3m4jlmFKRoTkkMy7ZZPP+LnQChmaztW+uj44oyfMb59daAs/Q==", + "requires": { + "csv-generate": "^4.3.1", + "csv-parse": "^5.5.3", + "csv-stringify": "^6.4.5", + "stream-transform": "^3.3.0" + } + }, + "csv-generate": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.3.1.tgz", + "integrity": "sha512-7YeeJq+44/I/O5N2sr2qBMcHZXhpfe38eh7DOFxyMtYO+Pir7kIfgFkW5MPksqKqqR6+/wX7UGoZm1Ot11151w==" + }, + "csv-parse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.5.3.tgz", + "integrity": "sha512-v0KW6C0qlZzoGjk6u5tLmVfyZxNgPGXZsWTXshpAgKVGmGXzaVWGdlCFxNx5iuzcXT/oJN1HHM9DZKwtAtYa+A==" + }, + "csv-stringify": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.4.5.tgz", + "integrity": "sha512-SPu1Vnh8U5EnzpNOi1NDBL5jU5Rx7DVHr15DNg9LXDTAbQlAVAmEbVt16wZvEW9Fu9Qt4Ji8kmeCJ2B1+4rFTQ==" + }, + "detect-libc": { + "version": "2.0.2" + }, + "dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==" + }, + "fast-xml-parser": { + "version": "4.2.5", + "requires": { + "strnum": "^1.0.5" + } + }, + "is-arrayish": { + "version": "0.3.2" + }, + "lru-cache": { + "version": "6.0.0", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "sharp": { + "version": "0.33.2", + "requires": { + "@img/sharp-darwin-arm64": "0.33.2", + "@img/sharp-darwin-x64": "0.33.2", + "@img/sharp-libvips-darwin-arm64": "1.0.1", + "@img/sharp-libvips-darwin-x64": "1.0.1", + "@img/sharp-libvips-linux-arm": "1.0.1", + "@img/sharp-libvips-linux-arm64": "1.0.1", + "@img/sharp-libvips-linux-s390x": "1.0.1", + "@img/sharp-libvips-linux-x64": "1.0.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.1", + "@img/sharp-libvips-linuxmusl-x64": "1.0.1", + "@img/sharp-linux-arm": "0.33.2", + "@img/sharp-linux-arm64": "0.33.2", + "@img/sharp-linux-s390x": "0.33.2", + "@img/sharp-linux-x64": "0.33.2", + "@img/sharp-linuxmusl-arm64": "0.33.2", + "@img/sharp-linuxmusl-x64": "0.33.2", + "@img/sharp-wasm32": "0.33.2", + "@img/sharp-win32-ia32": "0.33.2", + "@img/sharp-win32-x64": "0.33.2", + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "semver": "^7.5.4" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "stream-transform": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.0.tgz", + "integrity": "sha512-pG1NeDdmErNYKtvTpFayrEueAmL0xVU5wd22V7InGnatl4Ocq3HY7dcXIKj629kXvYQvglCC7CeDIGAlx1RNGA==" + }, + "strnum": { + "version": "1.0.5" + }, + "tslib": { + "version": "2.6.2" + }, + "yallist": { + "version": "4.0.0" + } + } +} diff --git a/housekeeping-scripts/s3-images-resizing/package.json b/housekeeping-scripts/s3-images-resizing/package.json new file mode 100644 index 000000000..3e0584dff --- /dev/null +++ b/housekeeping-scripts/s3-images-resizing/package.json @@ -0,0 +1,18 @@ +{ + "name": "eg-scripting", + "version": "1.0.0", + "description": "Nodejs based scripts for EG", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Manoj L", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.496.0", + "csv": "^6.3.6", + "dotenv": "^16.4.1", + "sharp": "^0.33.2" + } +} diff --git a/housekeeping-scripts/s3-images-resizing/resized/.gitkeep b/housekeeping-scripts/s3-images-resizing/resized/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/housekeeping-scripts/s3-images-resizing/resized/128x128/.gitkeep b/housekeeping-scripts/s3-images-resizing/resized/128x128/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/housekeeping-scripts/s3-images-resizing/resized/256x256/.gitkeep b/housekeeping-scripts/s3-images-resizing/resized/256x256/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/housekeeping-scripts/s3-images-resizing/resized/32x32/.gitkeep b/housekeeping-scripts/s3-images-resizing/resized/32x32/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/housekeeping-scripts/s3-images-resizing/resized/64x64/.gitkeep b/housekeeping-scripts/s3-images-resizing/resized/64x64/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/housekeeping-scripts/s3-images-resizing/sample.csv b/housekeeping-scripts/s3-images-resizing/sample.csv new file mode 100644 index 000000000..8c0e74ff9 --- /dev/null +++ b/housekeeping-scripts/s3-images-resizing/sample.csv @@ -0,0 +1,2 @@ +"id","name","provider","path","doument_type" +52,"abc.jpeg","s3","/user/docs","profile" \ No newline at end of file diff --git a/src/.env.example b/src/.env.example index 823676203..5817618be 100644 --- a/src/.env.example +++ b/src/.env.example @@ -1,10 +1,18 @@ OTP_EXPIRY_IN_MINUTES=10 # Sentry -SENTRY_DSN_URL="" -SENTRY_ENVIRONMENT="local" +SENTRY_DSN_URL: +SENTRY_ENVIRONMENT: local # SMS Gateway -SMS_GATEWAY_BASE_URL="" -SMS_GATEWAY_API_KEY="" -SMS_GATEWAY_SENDER_ID="" \ No newline at end of file +SMS_GATEWAY_BASE_URL: +SMS_GATEWAY_API_KEY: +SMS_GATEWAY_SENDER_ID: + +# Caching +CACHE_SERVICE_HOST: localhost +CACHE_REDIS_PASSWORD: +CACHE_DEFAULT_TTL: 900 +CACHE_ACCESS_CONTROL_TTL: 300 +CACHE_ENUM_TTL: 900 +CACHE_GEOLOCATION_TTL: 900 diff --git a/src/package-lock.json b/src/package-lock.json index 37f328d6c..2c9153ceb 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -18,12 +18,12 @@ "@nestjs/config": "^2.3.1", "@nestjs/core": "^9.0.0", "@nestjs/mapped-types": "^1.2.2", - "@nestjs/platform-express": "^9.0.0", + "@nestjs/platform-express": "^9.4.3", "@nestjs/schedule": "^3.0.1", "@sentry/node": "^7.52.1", "atob": "^2.1.2", "aws-sdk": "^2.1454.0", - "axios": "^1.3.4", + "axios": "^1.6.7", "cache-manager": "^3.6.0", "cache-manager-redis-store": "^2.0.0", "class-transformer": "^0.5.1", diff --git a/src/package.json b/src/package.json index f17bbb1ad..62ef83d34 100644 --- a/src/package.json +++ b/src/package.json @@ -29,12 +29,12 @@ "@nestjs/config": "^2.3.1", "@nestjs/core": "^9.0.0", "@nestjs/mapped-types": "^1.2.2", - "@nestjs/platform-express": "^9.0.0", + "@nestjs/platform-express": "^9.4.3", "@nestjs/schedule": "^3.0.1", "@sentry/node": "^7.52.1", "atob": "^2.1.2", "aws-sdk": "^2.1454.0", - "axios": "^1.3.4", + "axios": "^1.6.7", "cache-manager": "^3.6.0", "cache-manager-redis-store": "^2.0.0", "class-transformer": "^0.5.1", diff --git a/src/src/app.module.ts b/src/src/app.module.ts index 2680837cf..24019b357 100755 --- a/src/src/app.module.ts +++ b/src/src/app.module.ts @@ -38,6 +38,8 @@ import { UploadFileModule } from './upload-file/upload-file.module'; import { UserModule } from './user/user.module'; import { UserauthModule } from './userauth/userauth.module'; import { BoardModule } from './modules/board/board.module'; +import { OrganisationModule } from './organisation/organisation.module'; +import { ObservationsModule } from './observations/observations.module'; @Module({ imports: [ ScheduleModule.forRoot(), @@ -85,6 +87,8 @@ import { BoardModule } from './modules/board/board.module'; UploadFileModule, UserModule, UserauthModule, + OrganisationModule, + ObservationsModule, ], controllers: [], providers: [CacheCleanerProvider], diff --git a/src/src/attendances/attendances.core.service.ts b/src/src/attendances/attendances.core.service.ts index abd9c3c98..c69548b94 100644 --- a/src/src/attendances/attendances.core.service.ts +++ b/src/src/attendances/attendances.core.service.ts @@ -231,15 +231,22 @@ export class AttendancesCoreService { return result; } - async getUserAttendancePresentList(user_id, context, context_id) { + async getUserAttendancePresentList({ + user_id, + context, + context_id, + event_end_date, + event_start_date, + }) { const query = `query MyQuery { - attendance(where: {user_id: {_eq: ${user_id}}, context: {_eq: ${context}}, context_id: {_eq:${context_id}}, status: {_eq: "present"}}) { + attendance(where: {user_id: {_eq: ${user_id}}, context: {_eq: ${context}}, context_id: {_eq:${context_id}}, status: {_eq: "present"}, date_time: {_gte: "${event_start_date}", _lte: "${event_end_date}"}}, distinct_on: date_time) { id status context context_id } }`; + try { const result_response = await this.hasuraServiceFromServices.getData({ query }); diff --git a/src/src/beneficiaries/beneficiaries.controller.ts b/src/src/beneficiaries/beneficiaries.controller.ts index f0475a01a..13bae3605 100644 --- a/src/src/beneficiaries/beneficiaries.controller.ts +++ b/src/src/beneficiaries/beneficiaries.controller.ts @@ -220,11 +220,31 @@ export class BeneficiariesController { @Get(':id') @UseGuards(new AuthGuard()) - findOne( + public async findOne( @Param('id') id: string, @Req() req: any, @Res() response: Response, ) { + if (req.mw_roles?.includes('program_owner')) { + req.parent_ip_id = req.mw_ip_user_id; + } else { + const user = await this.userService.ipUserInfo(req); + if (req.mw_roles?.includes('staff')) { + req.parent_ip_id = + user?.data?.program_users?.[0]?.organisation_id; + } else if (req.mw_roles?.includes('facilitator')) { + req.parent_ip_id = user?.data?.program_faciltators?.parent_ip; + } + } + + if (!req.parent_ip_id) { + return response.status(404).send({ + success: false, + message: 'Invalid Ip', + data: {}, + }); + } + return this.beneficiariesService.findOne(+id, response); } diff --git a/src/src/beneficiaries/beneficiaries.module.ts b/src/src/beneficiaries/beneficiaries.module.ts index 7ee1ddd7b..791cb5e62 100644 --- a/src/src/beneficiaries/beneficiaries.module.ts +++ b/src/src/beneficiaries/beneficiaries.module.ts @@ -20,6 +20,7 @@ import { HasuraModule as HasuraModuleFromServices } from '../services/hasura/has import { KeycloakModule } from '../services/keycloak/keycloak.module'; import { BeneficiariesCoreService } from './beneficiaries.core.service'; import { BeneficiariesService } from './beneficiaries.service'; +import { IpMiddleware } from 'src/common/middlewares/ip.middleware'; @Module({ imports: [ UserModule, @@ -74,13 +75,15 @@ export class BeneficiariesModule implements NestModule { ); */ - + consumer + .apply(IpMiddleware) + .forRoutes('/beneficiaries/admin/list', '/beneficiaries/:id'); consumer .apply(CohortMiddleware) .exclude( '/beneficiaries/admin/list/duplicates-by-aadhaar', '/beneficiaries/:id/is_enrollment_exists', - { path: '/beneficiaries/:id', method: RequestMethod.GET }, + { path: '/beneficiaries/:id', method: RequestMethod.DELETE }, '/beneficiaries/register', '/beneficiaries/statusUpdate', @@ -88,10 +91,9 @@ export class BeneficiariesModule implements NestModule { ) .forRoutes(BeneficiariesController); - consumer - .apply(CohortMiddleware) - .forRoutes( - {path: '/beneficiaries/getStatusWiseCount', method: RequestMethod.GET,} - ); + consumer.apply(CohortMiddleware).forRoutes({ + path: '/beneficiaries/getStatusWiseCount', + method: RequestMethod.GET, + }); } } diff --git a/src/src/beneficiaries/beneficiaries.service.ts b/src/src/beneficiaries/beneficiaries.service.ts index c720b8ad7..327cf672e 100644 --- a/src/src/beneficiaries/beneficiaries.service.ts +++ b/src/src/beneficiaries/beneficiaries.service.ts @@ -681,10 +681,20 @@ export class BeneficiariesService { } public async getList(body: any, req: any, resp: any) { - const user = await this.userService.ipUserInfo(req); const program_id = req.mw_program_id; const academic_year_id = req.mw_academic_year_id; - if (!user?.data?.program_users?.[0]?.organisation_id) { + if (req.mw_roles?.includes('program_owner')) { + req.parent_ip_id = req.mw_ip_user_id; + } else { + const user = await this.userService.ipUserInfo(req); + if (req.mw_roles?.includes('staff')) { + req.parent_ip_id = + user?.data?.program_users?.[0]?.organisation_id; + } else if (req.mw_roles?.includes('facilitator')) { + req.parent_ip_id = user?.data?.program_faciltators?.parent_ip; + } + } + if (!req.parent_ip_id) { return resp.status(404).send({ success: false, message: 'Invalid Ip', @@ -693,18 +703,18 @@ export class BeneficiariesService { } const sortType = body?.sortType ? body?.sortType : 'desc'; const page = isNaN(body.page) ? 1 : parseInt(body.page); - const limit = isNaN(body.limit) ? 15 : parseInt(body.limit); + const limit = isNaN(body.limit) ? 10 : parseInt(body.limit); let offset = page > 1 ? limit * (page - 1) : 0; let status = body?.status; let filterQueryArray = []; if (body?.reassign) { filterQueryArray.push( - `{_not: {group_users: {status: {_eq: "active"}, group: {status: {_in: ["registered", "camp_ip_verified", "change_required"]}}}}},{ program_beneficiaries: {facilitator_user: { program_faciltators: { parent_ip: { _eq: "${user?.data?.program_users[0]?.organisation_id}" } ,program_id:{_eq:${program_id}},academic_year_id:{_eq:${academic_year_id}}} } } }`, + `{_not: {group_users: {status: {_eq: "active"}, group: {status: {_in: ["registered", "camp_ip_verified", "change_required"]}}}}},{ program_beneficiaries: {facilitator_user: { program_faciltators: { parent_ip: { _eq: "${req.parent_ip_id}" } ,program_id:{_eq:${program_id}},academic_year_id:{_eq:${academic_year_id}}} } } }`, ); } else { filterQueryArray.push( - `{ program_beneficiaries: {facilitator_user: { program_faciltators: { parent_ip: { _eq: "${user?.data?.program_users[0]?.organisation_id}" },program_id:{_eq:${program_id}},academic_year_id:{_eq:${academic_year_id}} }},academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}} } }`, + `{ program_beneficiaries: {facilitator_user: { program_faciltators: { parent_ip: { _eq: "${req.parent_ip_id}" },program_id:{_eq:${program_id}},academic_year_id:{_eq:${academic_year_id}} }},academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}} } }`, ); } diff --git a/src/src/camp/camp.controller.ts b/src/src/camp/camp.controller.ts index 10786f27e..a34f435e8 100644 --- a/src/src/camp/camp.controller.ts +++ b/src/src/camp/camp.controller.ts @@ -384,4 +384,30 @@ export class CampController { campDetails(@Req() request: any, @Body() body: any, @Res() response: any) { return this.campService.campDetails(body, request, response); } + + @Post('camp-info/learners') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getCampLearnersListForEPCP( + @Res() response: Response, + @Req() request: Request, + ) { + return this.campService.getCampLearnersListForEPCP(response, request); + } + + @Post('admin/end_pcr') + @UseGuards(new AuthGuard()) + pcrCampEnd(@Req() request: any, @Body() body: any, @Res() response: any) { + return this.campService.pcrCampEnd(body, request, response); + } + + @Post('admin/multiple_end_pcr') + @UseGuards(new AuthGuard()) + multiplePcrCampEnd( + @Req() request: any, + @Body() body: any, + @Res() response: any, + ) { + return this.campService.multiplePcrCampEnd(body, request, response); + } } diff --git a/src/src/camp/camp.core.service.ts b/src/src/camp/camp.core.service.ts index 5d39ca188..e6655a979 100644 --- a/src/src/camp/camp.core.service.ts +++ b/src/src/camp/camp.core.service.ts @@ -78,17 +78,20 @@ export class CampCoreService { .data.map((item) => item.value); const page = isNaN(body.page) ? 1 : parseInt(body.page); - const limit = isNaN(body.limit) ? 15 : parseInt(body.limit); + const limit = isNaN(body.limit) ? 10 : parseInt(body.limit); let offset = page > 1 ? limit * (page - 1) : 0; const program_id = req.mw_program_id; const academic_year_id = req.mw_academic_year_id; - let parent_ip_id = body?.parent_ip_id; + let parent_ip_id = req?.parent_ip_id; let status = body?.status; - + const type = []; filterQueryArray.push( `{group_users: {member_type: {_eq: "owner"}, group: {program_id: {_eq:${program_id}}, academic_year_id: {_eq:${academic_year_id}}},user:{program_faciltators:{parent_ip:{_eq:"${parent_ip_id}"}}}}}`, ); + if (body?.type && body?.type.length > 0) { + type.push(`type:{_in: ${JSON.stringify(body?.type)}}`); + } if (body?.state && body?.state.length > 0) { filterQueryArray.push( `{properties:{state:{_in: ${JSON.stringify(body?.state)}}}}`, @@ -133,7 +136,12 @@ export class CampCoreService { filterQueryArray.push(`{group:{status:{_eq:"${status}"}}}`); } - let filterQuery = '{ _and: [' + filterQueryArray.join(',') + '] }'; + let filterQuery = + '{' + + type.join(',') + + ' _and: [' + + filterQueryArray.join(',') + + '] }'; let data = { query: `query MyQuery($limit: Int, $offset: Int) { @@ -148,6 +156,7 @@ export class CampCoreService { kit_was_sufficient kit_ratings kit_feedback + type properties { district block diff --git a/src/src/camp/camp.module.ts b/src/src/camp/camp.module.ts index 67bd17a66..b8e3dc769 100644 --- a/src/src/camp/camp.module.ts +++ b/src/src/camp/camp.module.ts @@ -20,6 +20,7 @@ import { CampController } from './camp.controller'; import { CampCoreService } from './camp.core.service'; import { CampService } from './camp.service'; import { Method } from 'src/common/method/method'; +import { IpMiddleware } from 'src/common/middlewares/ip.middleware'; @Module({ imports: [ @@ -41,6 +42,9 @@ import { Method } from 'src/common/method/method'; }) export class CampModule implements NestModule { configure(consumer: MiddlewareConsumer) { + consumer + .apply(IpMiddleware) + .forRoutes('/camp/admin/camp-details/:id', '/camp/admin/camp-list'); consumer .apply(CohortMiddleware) .exclude( diff --git a/src/src/camp/camp.service.ts b/src/src/camp/camp.service.ts index 400ba0616..8bf26a6e1 100644 --- a/src/src/camp/camp.service.ts +++ b/src/src/camp/camp.service.ts @@ -36,6 +36,7 @@ export class CampService { 'kit_ratings', 'kit_feedback', 'group_id', + 'type', ]; public returnFieldsconsents = [ @@ -193,6 +194,7 @@ export class CampService { group_id: createresponse?.groups?.id, created_by: facilitator_id, updated_by: facilitator_id, + type: 'pcr', }; createcampResponse = await this.hasuraService.q( @@ -382,7 +384,7 @@ export class CampService { let status = 'active'; let qury = `query MyQuery { - camps(where: {group_users: {group: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}}}, user: {}, member_type: {_eq:${member_type}}, status: {_eq:${status}}, user_id: {_eq:${facilitator_id}}}},order_by: {id: asc}) { + camps:camps(where: {group_users: {group: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}}}, user: {}, member_type: {_eq:${member_type}}, status: {_eq:${status}}, user_id: {_eq:${facilitator_id}}},type:{_eq:"main"}},order_by: {id: asc}) { id kit_ratings kit_feedback @@ -391,6 +393,7 @@ export class CampService { preferred_start_time preferred_end_time week_off + type group{ name description @@ -404,16 +407,42 @@ export class CampService { } } + pcr_camp:camps(where: {group_users: {group: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}}}, user: {}, member_type: {_eq:${member_type}}, status: {_eq:${status}}, user_id: {_eq:${facilitator_id}}},type:{_eq:"pcr"}},order_by: {id: asc}) { + id + kit_ratings + kit_feedback + kit_received + kit_was_sufficient + preferred_start_time + preferred_end_time + week_off + type + group{ + name + description + status + } + group_users(where: {member_type: {_neq: "owner"}}) { + user_id + status + member_type + + } + } }`; const data = { query: qury }; const response = await this.hasuraServiceFromServices.getData(data); - const newQdata = response?.data; + const camps = response?.data?.camps || []; + const pcr_camp = response?.data?.pcr_camp || []; return resp.status(200).json({ success: true, message: 'Data found successfully!', - data: newQdata || { camps: [] }, + data: { + camps, + pcr_camp, + }, }); } @@ -435,6 +464,7 @@ export class CampService { preferred_start_time preferred_end_time week_off + type group{ name description @@ -787,6 +817,7 @@ export class CampService { preferred_start_time preferred_end_time week_off + type properties { lat long @@ -2030,9 +2061,19 @@ export class CampService { let facilitator_id = body?.facilitator_id; const program_id = request.mw_program_id; const academic_year_id = request.mw_academic_year_id; + const page = isNaN(body.page) ? 1 : parseInt(body.page); + const limit = isNaN(body.limit) ? 50 : parseInt(body.limit); + let offset = page > 1 ? limit * (page - 1) : 0; - let query = `query MyQuery { - consents(where: {facilitator_id: {_eq:${facilitator_id}},camp_id: {_eq:${camp_id}}, status: {_eq: "active"},academic_year_id: {_eq:${academic_year_id}},program_id: {_eq:${program_id}}}) { + let data = { + query: `query MyQuery($limit:Int, $offset:Int) { + consents_aggregate(where: {facilitator_id: {_eq:${facilitator_id}},camp_id: {_eq:${camp_id}}, status: {_eq: "active"},academic_year_id: {_eq:${academic_year_id}},program_id: {_eq:${program_id}}}) { + aggregate { + count + } + } + consents(limit: $limit, + offset: $offset,where: {facilitator_id: {_eq:${facilitator_id}},camp_id: {_eq:${camp_id}}, status: {_eq: "active"},academic_year_id: {_eq:${academic_year_id}},program_id: {_eq:${program_id}}}) { id document_id user_id @@ -2041,12 +2082,23 @@ export class CampService { name } } - }`; + }`, + variables: { + limit: limit, + offset: offset, + }, + }; - const hasura_response = await this.hasuraServiceFromServices.getData({ - query: query, - }); + const hasura_response = await this.hasuraServiceFromServices.getData( + data, + ); const consent_response = hasura_response?.data; + + const count = + hasura_response?.data?.consents_aggregate?.aggregate?.count; + + const totalPages = Math.ceil(count / limit); + if (!consent_response?.consents?.length) { return resp.status(200).json({ success: true, @@ -2068,13 +2120,16 @@ export class CampService { status: 200, message: 'Successfully updated consents details', data: resultData, + totalCount: count, + limit: limit, + currentPage: page, + totalPages: totalPages, }); } } async getAdminConsentBenficiaries(body: any, request: any, resp: any) { let camp_id = body?.camp_id; - const user = await this.userService.ipUserInfo(request); if (!user?.data?.program_users?.[0]?.organisation_id) { @@ -2091,15 +2146,13 @@ export class CampService { // get facilitator for the provided camp id - let query = `query MyQuery { + let query = `query MyQuery{ camps(where: {id: {_eq:${camp_id}}, group_users: {user: {program_faciltators: {parent_ip: {_eq: "${parent_ip_id}"}}}}}) { group_users(where: {member_type: {_eq: "owner"}, status: {_eq: "active"}}) { user_id } } - } - - `; + }`; const hasura_response = await this.hasuraServiceFromServices.getData({ query: query, @@ -2211,6 +2264,7 @@ export class CampService { } if (!group_id) { return resp.status(400).json({ + status: 400, message: 'CAMP_INVALID_ERROR', data: [], }); @@ -2233,6 +2287,7 @@ export class CampService { ); return resp.status(200).json({ + status: 200, message: 'Successfully updated camp details', data: camp_id, }); @@ -2240,15 +2295,25 @@ export class CampService { } async getCampList(body: any, req: any, resp: any) { - const user = await this.userService.ipUserInfo(req); - if (!user?.data?.program_users?.[0]?.organisation_id) { + if (req.mw_roles?.includes('program_owner')) { + req.parent_ip_id = req.mw_ip_user_id; + } else { + const user = await this.userService.ipUserInfo(req); + if (req.mw_roles?.includes('staff')) { + req.parent_ip_id = + user?.data?.program_users?.[0]?.organisation_id; + } else if (req.mw_roles?.includes('facilitator')) { + req.parent_ip_id = user?.data?.program_faciltators?.parent_ip; + } + } + if (!req.parent_ip_id) { return resp.status(404).send({ success: false, message: 'Invalid Ip', data: {}, }); } - body.parent_ip_id = user?.data?.program_users?.[0]?.organisation_id; + const data = await this.campcoreservice.list(body, req); if (data) { @@ -2276,9 +2341,19 @@ export class CampService { data: [], }); } - const user = await this.userService.ipUserInfo(req); - - if (!user?.data?.program_users?.[0]?.organisation_id) { + if (req.mw_roles?.includes('program_owner')) { + req.parent_ip_id = req.mw_ip_user_id; + } else { + const user = await this.userService.ipUserInfo(req); + if (req.mw_roles?.includes('staff')) { + req.parent_ip_id = + user?.data?.program_users?.[0]?.organisation_id; + } else if (req.mw_roles?.includes('facilitator')) { + req.parent_ip_id = + user?.data?.program_faciltators?.parent_ip; + } + } + if (!req.parent_ip_id) { return resp.status(404).send({ success: false, message: 'Invalid Ip', @@ -2293,6 +2368,7 @@ export class CampService { kit_was_sufficient kit_ratings kit_feedback + type group { name status @@ -3463,6 +3539,7 @@ export class CampService { let camp_day_happening = body?.camp_day_happening; let camp_day_not_happening_reason = body?.camp_day_not_happening_reason; let mood = body?.mood; + const camp_type = body?.camp_type; let object; const currentDate = moment().format('YYYY-MM-DD HH:mm:ss'); @@ -3503,9 +3580,9 @@ export class CampService { } if (camp_day_happening === 'no') { - object = `{camp_id: ${camp_id}, camp_day_happening: "${camp_day_happening}", camp_day_not_happening_reason: "${camp_day_not_happening_reason}", created_by: ${created_by}, updated_by: ${updated_by}, start_date: "${currentDate}",end_date:"${currentDate}"}`; + object = `{camp_id: ${camp_id}, camp_day_happening: "${camp_day_happening}",camp_type:"${camp_type}", camp_day_not_happening_reason: "${camp_day_not_happening_reason}", created_by: ${created_by}, updated_by: ${updated_by}, start_date: "${currentDate}",end_date:"${currentDate}"}`; } else { - object = `{camp_id: ${camp_id}, camp_day_happening: "${camp_day_happening}", created_by: ${created_by}, updated_by: ${updated_by}, start_date: "${currentDate}", mood: "${mood}"}`; + object = `{camp_id: ${camp_id}, camp_day_happening: "${camp_day_happening}",camp_type:"${camp_type}", created_by: ${created_by}, updated_by: ${updated_by}, start_date: "${currentDate}", mood: "${mood}"}`; } const data = { @@ -3521,7 +3598,7 @@ export class CampService { mood start_date end_date - + camp_type } }`, }; @@ -4513,4 +4590,234 @@ export class CampService { }); } } + + async getCampLearnersListForEPCP(response: any, request: any) { + let program_id = request?.mw_program_id; + let academic_year_id = request?.mw_academic_year_id; + let user_id = request?.mw_userid; + let query = `query MyQuery { + camps(where: {group: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}},status: {_in: ["registered","camp_ip_verified","change_required"]}, group_users: {user_id: {_eq:${user_id}}, member_type: {_eq: "owner"}, status: {_eq: "active"}}}}) { + camp_id: id + group { + group_id: id + group_users(where: {member_type: {_eq: "member"}, status: {_eq: "active"}, user: {program_beneficiaries: {status: {_eq: "registered_in_camp"}}}}) { + user { + user_id: id + first_name + middle_name + last_name + } + } + } + } + } + + + `; + + const result = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + const newQdata = result?.data?.camps; + + if (newQdata) { + return response.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return response.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + public async pcrCampEnd(body: any, request: any, response: any) { + const camp_id = body?.camp_id; + const user = await this.userService.ipUserInfo(request); + if (!user?.data?.program_users?.[0]?.organisation_id) { + return request.status(404).send({ + success: false, + message: 'Invalid Ip', + data: {}, + }); + } + let ip_id = user?.data?.program_users?.[0]?.organisation_id; + //validation check is camp type and camp-day-activity is PCR type + let data = { + query: `query MyQuery { + camps:camps(where: {id: {_eq: ${camp_id}}}){ + id + type + } + }`, + }; + + const pcr_response = await this.hasuraServiceFromServices.getData(data); + //check camps type is PCR or not! + const camps = pcr_response?.data?.camps[0]?.type; + const type = 'main'; + if (camps === 'pcr') { + let update_body = ['type']; + let camp_day_response = await this.hasuraService.q( + 'camps', + { + ...body, + id: camp_id, + type: 'main', + }, + update_body, + true, + ['id', 'type'], + ); + let camp = camp_day_response?.camps; + + // activity logs old camp to new camp + const auditData = { + userId: request?.mw_userid, + mw_userid: request?.mw_userid, + user_type: 'IP', + context: 'pcr_camp.update.camp_type', + context_id: camp_id, + oldData: { + camp_id: camp_id, + type: camps, + }, + newData: { + camp_id: camp_id, + type: 'main', + }, + subject: 'pcr_camp', + subject_id: camp_id, + log_transaction_text: `IP ${request.mw_userid} change pcr camps type pcr to ${type}.`, + tempArray: ['camp_id', 'camps'], + action: 'update', + sortedData: true, + }; + await this.userService.addAuditLogAction(auditData); + + if (camp) { + return response.status(200).json({ + success: true, + message: 'PCR camp updated successfully!', + data: { + camp, + }, + }); + } + } else { + return response.status(422).json({ + success: false, + message: 'PCR Camp is Already close', + data: {}, + }); + } + } + + //multiple PCR camp END + public async multiplePcrCampEnd(body: any, request: any, response: any) { + const camp_id = body?.camp_id; + const user = await this.userService.ipUserInfo(request); + if (!user?.data?.program_users?.[0]?.organisation_id) { + return request.status(404).send({ + success: false, + message: 'Invalid Ip', + data: {}, + }); + } + let ip_id = user?.data?.program_users?.[0]?.organisation_id; + // Check if camp_id is provided and is an array + if (!camp_id || !Array.isArray(camp_id) || camp_id.length === 0) { + return response.status(422).json({ + success: false, + message: + 'camp_id is required and must be an array and should not be empty', + data: {}, + }); + } + //validation check is camp type is PCR only + let data = { + query: `query MyQuery { + camps:camps(where: {id: {_in: [${camp_id}]}}){ + id + type + } +}`, + }; + const pcr_response = await this.hasuraServiceFromServices.getData(data); + //check camps type is PCR or not! + const camps = pcr_response?.data?.camps ?? []; + + // Check if all camps are of type PCR + const campIds = camps + .filter((camp: any) => camp.type != 'pcr') + .map((e) => e.id); + + if (campIds.length > 0) { + return response.status(422).json({ + success: false, + message: `${campIds} This ID are Not PCR Camp Ids`, + data: {}, + }); + } + const type = 'main'; + + let updatecamp = { + query: `mutation MyMutation { + update_camps_many(updates: {where: {id: {_in: [${camp_id}]}}, _set: {type: "main"}}) { + affected_rows + returning { + id + type + } + } + }`, + }; + const updateResponse = await this.hasuraServiceFromServices.getData( + updatecamp, + ); + const updatedCamps = + updateResponse?.data?.update_camps_many?.[0].returning ?? []; + + // activity logs old camp to new camp + const userData = await Promise.all( + updatedCamps?.map(async (item) => { + const auditData = { + userId: request?.mw_userid, + mw_userid: request?.mw_userid, + user_type: 'IP', + context: 'camps.update.camp_type', + context_id: item.id, + oldData: { + camp_id: item.id, + type: 'pcr', + }, + newData: { + camp_id: item.id, + type: item.type, + }, + subject: 'camps', + subject_id: item.id, + log_transaction_text: `IP ${request.mw_userid} change pcr camps type pcr to ${type}.`, + tempArray: ['camp_id', 'camps'], + action: 'update', + sortedData: true, + }; + await this.userService.addAuditLogAction(auditData); + }), + ); + if (updatedCamps) { + return response.status(200).json({ + success: true, + message: 'PCR camp updated successfully!', + data: { + updatedCamps, + }, + }); + } + } } diff --git a/src/src/common/method/method.ts b/src/src/common/method/method.ts index 38f42c640..05cd5f092 100644 --- a/src/src/common/method/method.ts +++ b/src/src/common/method/method.ts @@ -20,6 +20,10 @@ export class Method { } public async isUserHasAccessForProgram(req: any) { + // if role is program_owner pass this access + if (req.mw_roles.includes('program_owner')) { + return true; + } // Set a table name let tableName; if (req.mw_roles.includes('staff')) { @@ -64,6 +68,10 @@ export class Method { } public async isUserHasAccessForAcademicYearId(req: any) { + // if role is program_owner pass this access + if (req.mw_roles.includes('program_owner')) { + return true; + } // Set a table name let tableName; if (req.mw_roles.includes('staff')) { diff --git a/src/src/common/middlewares/auth.middleware.ts b/src/src/common/middlewares/auth.middleware.ts index ca022fd8d..8cf4c0afb 100644 --- a/src/src/common/middlewares/auth.middleware.ts +++ b/src/src/common/middlewares/auth.middleware.ts @@ -6,6 +6,7 @@ import jwt_decode from 'jwt-decode'; @Injectable() export class AuthMiddleware implements NestMiddleware { constructor(private userService: UserService) {} + async use(req: any, res: Response, next: NextFunction) { req.mw_roles = []; req.mw_userid = null; @@ -47,16 +48,36 @@ export class AuthMiddleware implements NestMiddleware { try { const decoded: any = jwt_decode(authToken); let keycloak_id = decoded.sub; + let userId; + const roles = decoded.resource_access.hasura.roles || []; + //check if role is program_owner set x-ip-org-id in userId + // if (roles.includes('program_owner')) { + // if (req?.headers && req?.headers?.['x-ip-org-id']) { + // userId = req.headers['x-ip-org-id']; + // const result = await this.userService.getIPuser( + // req, + // res, + // ); + // const data = result?.data?.program_users?.[0]; + // req.mw_userid = data?.user_id; + // } else if (['/users/ip_user_info'].includes(req.baseUrl)) { + // // const user = await this.userService.ipUserInfo(req); + // userId = await this.userService.getUserIdFromKeycloakId( + // keycloak_id, + // ); + // req.mw_userid = userId; + // } + // req.mw_roles = roles; //pass role if x-ip-org-id is not send + // } else { // const user = await this.userService.ipUserInfo(req); - const userId = await this.userService.getUserIdFromKeycloakId( + userId = await this.userService.getUserIdFromKeycloakId( keycloak_id, ); - req.mw_userid = userId; - + // } if (userId) { - req.mw_roles = decoded.resource_access.hasura.roles || []; + req.mw_roles = roles; } } catch (error) { req.mw_userid = null; diff --git a/src/src/common/middlewares/cohort.middleware.ts b/src/src/common/middlewares/cohort.middleware.ts index f18c9d8bf..78b916dba 100644 --- a/src/src/common/middlewares/cohort.middleware.ts +++ b/src/src/common/middlewares/cohort.middleware.ts @@ -11,8 +11,25 @@ export class CohortMiddleware implements NestMiddleware { constructor(private method: Method) {} async use(req: any, res: Response, next: () => void) { let goToNextMw = false; + //check IP User ID is present or not [x-ip-user-id] + // if ( + // req?.headers?.['x-ip-org-id'] || + // req?.mw_roles?.includes('program_owner') + // ) { + // req.mw_ip_user_id = req.headers['x-ip-org-id']; - if (req?.headers && req?.headers?.['x-program-id']) { + // const ip_user_id = parseInt(req.mw_ip_user_id, 10); + // if (isNaN(ip_user_id)) { + // return res.json({ + // success: false, + // message: `${ + // req.mw_ip_user_id ? 'Invalid' : 'Required' + // } Ip org Id`, + // }); + // } + // } + + if (req?.headers?.['x-program-id']) { req.mw_program_id = req.headers['x-program-id']; const program_id = parseInt(req.mw_program_id, 10); if (isNaN(program_id)) { @@ -34,7 +51,7 @@ export class CohortMiddleware implements NestMiddleware { }); } - if (req?.headers && req?.headers?.['x-academic-year-id']) { + if (req?.headers?.['x-academic-year-id']) { req.mw_academic_year_id = req.headers['x-academic-year-id']; const academic_year_id = parseInt(req.mw_academic_year_id, 10); if (isNaN(academic_year_id)) { diff --git a/src/src/common/middlewares/ip.middleware.ts b/src/src/common/middlewares/ip.middleware.ts new file mode 100644 index 000000000..e305bcd7c --- /dev/null +++ b/src/src/common/middlewares/ip.middleware.ts @@ -0,0 +1,33 @@ +import { + BadRequestException, + Injectable, + NestMiddleware, +} from '@nestjs/common'; +import { Response } from 'express'; +import { Method } from '../method/method'; + +@Injectable() +export class IpMiddleware implements NestMiddleware { + constructor(private method: Method) {} + async use(req: any, res: Response, next: () => void) { + //check IP User ID is present or not [x-ip-user-id] + if ( + req?.headers?.['x-ip-org-id'] || + req?.mw_roles?.includes('program_owner') + ) { + req.mw_ip_user_id = req.headers['x-ip-org-id']; + + const ip_user_id = parseInt(req.mw_ip_user_id, 10); + if (isNaN(ip_user_id)) { + return res.json({ + success: false, + message: `${ + req.mw_ip_user_id ? 'Invalid' : 'Required' + } Ip org Id`, + }); + } + } + + next(); + } +} diff --git a/src/src/cron/campEnd.cron.ts b/src/src/cron/campEnd.cron.ts index b6a70c766..0f12fdf72 100644 --- a/src/src/cron/campEnd.cron.ts +++ b/src/src/cron/campEnd.cron.ts @@ -31,7 +31,7 @@ export class CampEndCron { system: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}}, _set: {end_camp_marked_by: "system", end_date: "${today}"}) { affected_rows } - } + } `; let result = await this.hasuraService.getData({ query: updateQuery }); diff --git a/src/src/cron/faAttendanceProcessing.cron.ts b/src/src/cron/faAttendanceProcessing.cron.ts index 28cc78605..19f2cb9b6 100644 --- a/src/src/cron/faAttendanceProcessing.cron.ts +++ b/src/src/cron/faAttendanceProcessing.cron.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Cron } from '@nestjs/schedule'; +import { Cron, CronExpression } from '@nestjs/schedule'; import { AwsRekognitionService } from '../services/aws-rekognition/aws-rekognition.service'; import { HasuraService } from '../services/hasura/hasura.service'; @@ -20,7 +20,7 @@ export class FaAttendanceProcessingCron { //3rd cron runs for each hour's 25th minute eg: 10:25am, 11::25am @Cron('25 * * * *') - async markAttendanceCron() { + async markAttendanceCron() { try { /*----------------------- Mark attendance of from face index of users in collection -----------------------*/ console.log( @@ -41,7 +41,7 @@ export class FaAttendanceProcessingCron { //console.dir(usersForAttendance, { depth: 99 }); // Step-2 Iterate thorugh them for (const user of usersForAttendance) { - const userId = String(user.id); + const userId = String(user.id); // Iterate through attendance documents and mark attendance for (const attendanceObj of user.attendances) { if ( @@ -62,50 +62,56 @@ export class FaAttendanceProcessingCron { ); //console.log('matchedUser', matchedUser); // Check if the user matched - let matchingPercentage = null; - const isMatchFound = (matchedUser as any[]).some( - (obj) => { - //console.log('obj', obj); - if ( - obj?.User?.UserId.replace( - this.prefixed, - '', - ) === userId - ) { - matchingPercentage = obj.Similarity; - return true; - } - }, - ); - //console.log('matchingPercentage', matchingPercentage); - // Set attendance verified as true or false based on results - let isAttendanceVerified = false; - if (isMatchFound) isAttendanceVerified = true; - /*console.log( - '-------------------------------------------------------------------------', - ); - console.log( - `------------------------------ Verified: ${isMatchFound} ----------------------------`, - ); - console.log( - '-------------------------------------------------------------------------', - );*/ - // Update in attendance data in database - await this.markAttendance(attendanceObj.id, { - isAttendanceVerified, - matchingPercentage, - }); - // Set delay between two attendance process - await new Promise((resolve, reject) => - setTimeout( - resolve, - parseInt( - this.configService.get( - 'AWS_REKOGNITION_MARK_ATTENDANCE_PROCESS_INTERVAL_TIME', + if (matchedUser === false) { + console.log( + '------------------------------ Verified: ProvisionedThroughputExceededException', + ); + } else { + let matchingPercentage = null; + const isMatchFound = (matchedUser as any[]).some( + (obj) => { + //console.log('obj', obj); + if ( + obj?.User?.UserId.replace( + this.prefixed, + '', + ) === userId + ) { + matchingPercentage = obj.Similarity; + return true; + } + }, + ); + //console.log('matchingPercentage', matchingPercentage); + // Set attendance verified as true or false based on results + let isAttendanceVerified = false; + if (isMatchFound) isAttendanceVerified = true; + /*console.log( + '-------------------------------------------------------------------------', + ); + console.log( + `------------------------------ Verified: ${isMatchFound} ----------------------------`, + ); + console.log( + '-------------------------------------------------------------------------', + );*/ + // Update in attendance data in database + await this.markAttendance(attendanceObj.id, { + isAttendanceVerified, + matchingPercentage, + }); + // Set delay between two attendance process + await new Promise((resolve, reject) => + setTimeout( + resolve, + parseInt( + this.configService.get( + 'AWS_REKOGNITION_MARK_ATTENDANCE_PROCESS_INTERVAL_TIME', + ), ), ), - ), - ); + ); + } } else { // Update in attendance data in database await this.markProcessed(attendanceObj.id); @@ -191,14 +197,14 @@ export class FaAttendanceProcessingCron { _and: [ { fa_user_indexed: {_eq: true} }, { attendances_aggregate: {count: {predicate: {_gt: 0}}} }, - { attendances: { fa_is_processed: {_is_null: true} } }, + { attendances: { fa_is_processed: {_is_null: true}, photo_1: {_is_null: false,_neq: "-"} } }, ] }, limit: ${limit} ) { id attendances ( where: { - fa_is_processed: {_is_null: true}, + fa_is_processed: {_is_null: true}, photo_1: {_is_null: false,_neq: "-"}, }) { id photo_1 diff --git a/src/src/cron/prepareCertificateHtml.cron.ts b/src/src/cron/prepareCertificateHtml.cron.ts index 249d20f31..b610ea63d 100644 --- a/src/src/cron/prepareCertificateHtml.cron.ts +++ b/src/src/cron/prepareCertificateHtml.cron.ts @@ -7,6 +7,7 @@ import { html_code } from 'src/lms/certificate_html'; import { LMSCertificateDto } from 'src/lms/dto/lms-certificate.dto'; import { UserService } from 'src/user/user.service'; import { HasuraService } from '../services/hasura/hasura.service'; +import { json } from 'stream/consumers'; const moment = require('moment'); const qr = require('qrcode'); @@ -26,6 +27,7 @@ export class PrepareCertificateHtmlCron { @Cron(CronExpression.EVERY_5_MINUTES) async prepareCertificateHtml() { console.log('cron job: issueCertificate started at time ' + new Date()); + //fetch all test tracking data which has certificate_status null const userForIssueCertificate = await this.fetchTestTrackingData( parseInt( @@ -48,42 +50,57 @@ export class PrepareCertificateHtmlCron { let test_id = userTestData?.test_id; let context = userTestData?.context; let context_id = userTestData?.context_id; - let getUserList = await this.userService.getUserName(user_id); - let user_name = ''; - if (getUserList.length > 0) { - user_name += getUserList[0]?.first_name - ? (await this.method.CapitalizeEachWord( - getUserList[0].first_name, - )) + ' ' - : ''; - user_name += getUserList[0]?.middle_name - ? (await this.method.CapitalizeEachWord( - getUserList[0].middle_name, - )) + ' ' - : ''; - user_name += getUserList[0]?.last_name - ? (await this.method.CapitalizeEachWord( - getUserList[0].last_name, - )) + ' ' - : ''; - } + + const user_name = await this.method.CapitalizeEachWord( + [ + userTestData?.user?.first_name, + userTestData?.user?.middle_name, + userTestData?.user?.last_name, + ] + .filter((e) => e) + .join(' '), + ); + + const event_start_date = moment( + userTestData?.events?.[0]?.start_date, + ).format('DD MMM YYYY'); + const event_end_date = moment( + userTestData?.events?.[0]?.end_date, + ).format('DD MMM YYYY'); + const academic_year = + userTestData?.events?.[0]?.academic_year?.name; + //get attendance status let attendance_valid = false; + const startMoment = moment( + userTestData?.events?.[0]?.start_date, + ); + const endMoment = moment(userTestData?.events?.[0]?.end_date); + let datesD = []; + while (startMoment.isSameOrBefore(endMoment)) { + datesD.push(startMoment.format('YYYY-MM-DD')); + startMoment.add(1, 'day'); + } + let usrAttendanceList = await this.attendanceCoreService.getUserAttendancePresentList( - user_id, - context, - context_id, + { + user_id, + context, + context_id, + event_start_date: `${userTestData?.events?.[0]?.start_date}T00:00:00`, + event_end_date: `${userTestData?.events?.[0]?.end_date}T23:59:59`, + }, ); + console.log('usrAttendanceList list', usrAttendanceList); - let minAttendance = parseInt( - this.configService.get( - 'LMS_CERTIFICATE_ISSUE_MIN_ATTENDANCE', - ), - ); + console.log('events-dates', JSON.stringify(datesD)); + let minAttendance = datesD.length; + if (usrAttendanceList.length >= minAttendance) { attendance_valid = true; } + //check certificate criteria if (userTestData?.score >= minPercentage && attendance_valid) { issue_status = 'true'; @@ -112,6 +129,18 @@ export class PrepareCertificateHtmlCron { let certificate_id = certificate_data?.id; let uid = 'P-' + certificate_id + '-' + user_id; //update html code + certificateTemplate = certificateTemplate.replace( + '{{academic_year}}', + academic_year, + ); + certificateTemplate = certificateTemplate.replace( + '{{event_start_date}}', + event_start_date, + ); + certificateTemplate = certificateTemplate.replace( + '{{event_end_date}}', + event_end_date, + ); certificateTemplate = certificateTemplate.replace( '{{name}}', user_name, @@ -203,10 +232,19 @@ export class PrepareCertificateHtmlCron { if (issue_status == 'true') { testTrackingUpdateData['certificate_status'] = issue_status; } - await this.updateTestTrackingData( + const result = await this.updateTestTrackingData( userTestData?.id, testTrackingUpdateData, ); + if (issue_status === 'false') { + console.log( + `user_id ${user_id} name ${user_name} testID ${test_id} Not Genrated event date count ${minAttendance} attendance count ${usrAttendanceList.length}`, + ); + } else if (result) { + console.log( + `user_id ${user_id} name ${user_name} testID ${test_id} Certificate Genrated Sucssefully`, + ); + } } } } @@ -247,13 +285,26 @@ export class PrepareCertificateHtmlCron { score context context_id + user{ + first_name + middle_name + last_name + } + events(where:{context:{_eq:"events"}}){ + id + start_date + end_date + academic_year{ + name + } + + } } } `; - console.log('fetchTestTrackingData query', query); + try { const result_query = await this.hasuraService.getData({ query }); - console.log('result_query', result_query); const data_list = result_query?.data?.lms_test_tracking; if (data_list) { return data_list; @@ -354,8 +405,6 @@ export class PrepareCertificateHtmlCron { } } async updateTestTrackingData(id, testTrackingUpdateData) { - console.log('id', id); - console.log('testTrackingUpdateData', testTrackingUpdateData); let setQuery = ``; if (testTrackingUpdateData?.certificate_status) { setQuery += `certificate_status: ${testTrackingUpdateData.certificate_status}`; diff --git a/src/src/enum/enum.json b/src/src/enum/enum.json index 534664bd3..4023e3fb4 100644 --- a/src/src/enum/enum.json +++ b/src/src/enum/enum.json @@ -154,22 +154,10 @@ "title": "FACILITATOR_STATUS_APPLIED", "value": "applied" }, - { - "title": "FACILITATOR_STATUS_APPLICATION_SCREENED", - "value": "application_screened" - }, - { - "title": "FACILITATOR_STATUS_SHORTLISTED_FOR_ORIENTATION", - "value": "shortlisted_for_orientation" - }, { "title": "FACILITATOR_STATUS_PRAGATI_MOBILIZER", "value": "pragati_mobilizer" }, - { - "title": "FACILITATOR_STATUS_SELECTED_FOR_TRAINING", - "value": "selected_for_training" - }, { "title": "FACILITATOR_STATUS_SELECTED_FOR_ONBOARDING", "value": "selected_for_onboarding" @@ -421,16 +409,20 @@ ], "FACILITATOR_EVENT_TYPE": [ { - "title": "FACILITATOR_EVENT_TYPE_PRERAK_ORIENTATION", - "value": "prerak_orientation" + "title": "FACILITATOR_EVENT_TYPE_PRAGATI_ORIENTATION", + "value": "pragati_orientation" }, { - "title": "FACILITATOR_EVENT_TYPE_PRERAK_FLN_TRAINING", - "value": "prerak_fln_training" + "title": "FACILITATOR_EVENT_TYPE_PCR_TRAINING", + "value": "pcr_training" }, { - "title": "FACILITATOR_EVENT_TYPE_PRERAK_CAMP_EXECUTION_TRAINING", - "value": "prerak_camp_execution_training" + "title": "FACILITATOR_EVENT_TYPE_MAIN_CAMP_EXECUTION_TRAINING", + "value": "main_camp_execution_training" + }, + { + "title": "FACILITATOR_EVENT_TYPE_DRIP_TRAINING", + "value": "drip_training" } ], "BENEFICIARY_REASONS_FOR_REJECTING_LEARNER": [ @@ -1410,6 +1402,10 @@ { "title": "MISCELLANEOUS_ACTIVITIES_SCHOOL_TEACHER_VISIT", "value": "miscellaneous_activities_school_teacher_visit" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_E_PCP_ACTIVITIES", + "value": "miscellaneous_activities_e_pcp_activities" } ], "KIT_MATERIALS_CHECKLISTS": [ @@ -1509,5 +1505,137 @@ "title": "PARENT_SUPPORT_MAY_BE", "value": "may_be" } + ], + "EVENT_BATCH_NAME": [ + { + "title": "EVENT_BATCH_NAME_BATCH_1", + "value": "batch_1" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_2", + "value": "batch_2" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_3", + "value": "batch_3" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_4", + "value": "batch_4" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_5", + "value": "batch_5" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_6", + "value": "batch_6" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_7", + "value": "batch_7" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_8", + "value": "batch_8" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_9", + "value": "batch_9" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_10", + "value": "batch_10" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_11", + "value": "batch_11" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_12", + "value": "batch_12" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_13", + "value": "batch_13" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_14", + "value": "batch_14" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_15", + "value": "batch_15" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_16", + "value": "batch_16" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_17", + "value": "batch_17" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_18", + "value": "batch_18" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_19", + "value": "batch_19" + }, + { + "title": "EVENT_BATCH_NAME_BATCH_20", + "value": "batch_20" + } + ], + "PCR_MISCELLANEOUS_ACTIVITIES": [ + { + "title": "MISCELLANEOUS_ACTIVITIES_COMMUNICATION_SKILLS", + "value": "miscellaneous_activities_communication_skills" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_ROLE_MODEL_INTERVENTION", + "value": "miscellaneous_activities_role_model_intervention" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_ASPIRATION_MAPPING", + "value": "miscellaneous_activities_aspiration_mapping" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_DIGITAL_LITERACY", + "value": "miscellaneous_activities_digital_literacy" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_FINANCIAL_LITERACY", + "value": "miscellaneous_activities_financial_literacy" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_LINKAGES", + "value": "miscellaneous_activities_linkages" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_FAMILY_VISIT", + "value": "miscellaneous_activities_family_visit" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_ROLE_MODEL_VISIT", + "value": "miscellaneous_activities_role_model_visit" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_STAKEHOLDER_VISIT", + "value": "miscellaneous_activities_stakeholder_visit" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_SCHOOL_TEACHER_VISIT", + "value": "miscellaneous_activities_school_teacher_visit" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_E_PCP_ACTIVITIES", + "value": "miscellaneous_activities_e_pcp_activities" + }, + { + "title": "MISCELLANEOUS_ACTIVITIES_PCR", + "value": "miscellaneous_activities_pcr" + } ] } diff --git a/src/src/events/dto/create-event.dto.ts b/src/src/events/dto/create-event.dto.ts index c5054948e..32029bbfe 100644 --- a/src/src/events/dto/create-event.dto.ts +++ b/src/src/events/dto/create-event.dto.ts @@ -1,46 +1,44 @@ // src/articles/dto/create-article.dto.ts -import { IsArray, IsNotEmpty, IsString } from 'class-validator'; +import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class CreateEventDto { + @IsString() + @IsNotEmpty() + public start_date: string; - @IsString() - @IsNotEmpty() - public start_date: string; + @IsString() + @IsNotEmpty() + public name: string; - @IsString() - @IsNotEmpty() - public name: string; - - @IsString() - @IsNotEmpty() - public end_date: string; + @IsString() + @IsNotEmpty() + public end_date: string; - @IsString() - @IsNotEmpty() - public end_time: string; + @IsString() + @IsNotEmpty() + public end_time: string; - @IsString() - @IsNotEmpty() - public location: string; + @IsString() + @IsOptional() + public location: string; - @IsString() - @IsNotEmpty() - public location_type: string; + @IsString() + @IsOptional() + public location_type: string; - @IsString() - @IsNotEmpty() - public start_time: string; + @IsString() + @IsNotEmpty() + public start_time: string; - public created_by: string; + public created_by: string; - public updated_by: string; + public updated_by: string; - @IsString() - @IsNotEmpty() - public type: string; + @IsString() + @IsNotEmpty() + public type: string; - @IsArray() - @IsNotEmpty() - public reminders: []; - + @IsArray() + @IsOptional() + public reminders: []; } diff --git a/src/src/events/events.controller.ts b/src/src/events/events.controller.ts index eb8fd09ae..c28cb60b1 100644 --- a/src/src/events/events.controller.ts +++ b/src/src/events/events.controller.ts @@ -35,10 +35,14 @@ export class EventsController { return this.eventsService.create(createEventDto, header, response); } - @Get('/list') + @Post('/list') @UseGuards(new AuthGuard()) - getEventsList(@Req() header: Request, @Res() response: Response) { - return this.eventsService.getEventsList(header, response); + getEventsList( + @Body() body: Body, + @Req() header: Request, + @Res() response: Response, + ) { + return this.eventsService.getEventsList(body, header, response); } @Post() diff --git a/src/src/events/events.service.ts b/src/src/events/events.service.ts index 8503c172b..e0b564633 100644 --- a/src/src/events/events.service.ts +++ b/src/src/events/events.service.ts @@ -74,6 +74,59 @@ export class EventsService { let academic_year_id = header?.mw_academic_year_id; const userDetail = await this.userService.ipUserInfo(header); let user_id = userDetail.data.id; + //get do_id for event exam master data + // Convert start_date and end_date to UTC + const startDateTimeUTC = moment + .tz( + req.start_date + ' ' + req.start_time, + 'YYYY-MM-DD HH:mm', + 'Asia/Kolkata', + ) + .utc(); + + const endDateTimeUTC = moment + .tz( + req.end_date + ' ' + req.end_time, + 'YYYY-MM-DD HH:mm', + 'Asia/Kolkata', + ) + .utc(); + + let eventExamData = { + query: `query MyQuery { + event_exams_master(where: {academic_year_id: {_eq: ${academic_year_id}}, program_id: {_eq: ${program_id}}, event_type: {_eq: "${req.type}"}}){ + id + do_id + event_type + } + }`, + }; + + const getExamId = await this.hasuraServiceFromServices.getData( + eventExamData, + ); + + let doIds = getExamId?.data?.event_exams_master?.map( + (exam) => exam.do_id, + ); + let paramId = null; + + if (doIds && doIds.length > 0) { + paramId = { + attendance_type: 'day', + do_id: doIds, + }; + } + let optional = {}; + if (req?.location && req?.location_type && req?.reminders) { + optional['location'] = req.location; + optional['reminders'] = JSON.stringify(req.reminders).replace( + /"/g, + '\\"', + ); + optional['location_type'] = req.location_type; + } + let obj = { ...(req?.context_id && { context_id: req?.context_id }), ...(req?.context && { context: req?.context }), @@ -81,61 +134,129 @@ export class EventsService { name: req.name, master_trainer: req.master_trainer, created_by: user_id, - end_date: req.end_date, - end_time: req.end_time, - location: req.location, - location_type: req.location_type, - start_date: req.start_date, - start_time: req.start_time, + end_date: endDateTimeUTC.format('YYYY-MM-DD'), + end_time: endDateTimeUTC.format('HH:mm'), + start_time: startDateTimeUTC.format('HH:mm'), + start_date: startDateTimeUTC.format('YYYY-MM-DD'), updated_by: user_id, type: req.type, - reminders: JSON.stringify(req.reminders).replace(/"/g, '\\"'), program_id: program_id, academic_year_id: academic_year_id, - params: req.params, + params: paramId, //set params to null if no param id found in the database + ...optional, //set optional for remainders,location,location_type }; - - const eventResult = await this.hasuraService.createWithVariable( - this.table, - obj, - this.returnFields, - [], - [{ key: 'params', type: 'json' }], + // Check duration + const daysDiff = moment(req.end_date).diff( + moment(req.start_date), + 'days', ); - if (eventResult) { - const promises = []; - const query = []; - for (const iterator of user_id_arr) { - let obj = { - user_id: iterator, - created_by: user_id, - context_id: eventResult.events.id, - context: 'events', - updated_by: user_id, - }; - query.push(obj); - } - for (const iterator of query) { - promises.push( - this.hasuraService.create( - 'attendance', - iterator, - this.attendanceReturnFields, - ), - ); + const currentDate = moment(); // Current date + + // Check if the number of attendees falls within the configured limits + let errorMessage = {}; + + const numAttendees = req.attendees.length; + const minParticipants = 0; // Example: Minimum number of participants allowed + const maxParticipants = 50; // Example: Maximum number of participants allowed + + if (numAttendees < minParticipants || numAttendees > maxParticipants) { + errorMessage = { + key: 'participants_limit', + massage: `Number of attendees must be between ${minParticipants} and ${maxParticipants}`, + }; + } else if (moment(req.start_date)?.isBefore(currentDate, 'day')) { + errorMessage = { + key: 'back_date', + message: 'start date is before the current date', + }; + } else if (daysDiff < 0 || daysDiff > 5) { + errorMessage = { + key: 'event_days', + message: 'Event duration must be between 1 and 5 days.', + }; + } + + if (Object.keys(errorMessage).length) { + return response.status(422).send({ + success: false, + ...errorMessage, + data: {}, + }); + } + + //checking the event already created + let data = { + query: `query MyQuery { + events_aggregate(where: {start_date: {_gte: "${req.start_date}", _lte: "${req.start_date}"}, end_date: {_gte: "${req.end_date}", _lte: "${req.end_date}"}, program_id: {_eq: ${program_id}}, academic_year_id: {_eq: ${academic_year_id}}, master_trainer: {_eq: "${req.master_trainer}"}, start_time: {_eq: "${req.start_time}"}, type: {_eq: "${req.type}"}, user_id: {_eq: ${user_id}}, name: {_eq: "${req.name}"}}) { + aggregate { + count + } } - const createAttendees = await Promise.all(promises); - let mappedData = createAttendees.map((data) => data.attendance); - if (createAttendees) { - return response.status(200).send({ - success: true, - message: 'Event created successfully!', - data: { - events: eventResult.events, - attendance: mappedData, - }, - }); + } + `, + }; + + const geteventData = await this.hasuraServiceFromServices.getData(data); + + const count = geteventData?.data?.events_aggregate?.aggregate?.count; + //if event created show this message + + if (count > 0) { + return response.status(422).send({ + success: false, + message: 'Event Already created!', + key: 'batch_name', + data: {}, + }); + } else { + const eventResult = await this.hasuraService.createWithVariable( + this.table, + obj, + this.returnFields, + [], + [{ key: 'params', type: 'json' }], + ); + if (eventResult) { + const promises = []; + const query = []; + for (const iterator of user_id_arr) { + let obj = { + user_id: iterator, + created_by: user_id, + context_id: eventResult.events.id, + context: 'events', + updated_by: user_id, + }; + query.push(obj); + } + for (const iterator of query) { + promises.push( + this.hasuraService.create( + 'attendance', + iterator, + this.attendanceReturnFields, + ), + ); + } + const createAttendees = await Promise.all(promises); + let mappedData = createAttendees.map((data) => data.attendance); + if (createAttendees) { + return response.status(200).send({ + success: true, + message: 'Event created successfully!', + data: { + events: eventResult.events, + attendance: mappedData, + }, + }); + } else { + return response.status(500).send({ + success: false, + message: 'Unable to create Event!', + data: {}, + }); + } } else { return response.status(500).send({ success: false, @@ -143,16 +264,11 @@ export class EventsService { data: {}, }); } - } else { - return response.status(500).send({ - success: false, - message: 'Unable to create Event!', - data: {}, - }); } } - public async getEventsList(header, response) { + public async getEventsList(body, header, response) { + let filter = []; let program_id = header?.mw_program_id; let academic_year_id = header?.mw_academic_year_id; const userDetail: any = await this.userService.ipUserInfo(header); @@ -180,6 +296,20 @@ export class EventsService { }); } + filter.push( + `{academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}}`, + ); + + if (body?.start_date) { + filter.push(`start_date: {_gte:"${body?.start_date}"}`); + } + + if (body?.end_date) { + filter.push(`end_date: {_lte:"${body?.end_date}"}`); + } else if (body?.start_date) { + filter.push(`end_date: {_lte:"${body?.start_date}"}`); + } + const allIpList = getIps?.data?.users.map((curr) => curr.id); let getQuery = { query: `query MyQuery { @@ -196,7 +326,7 @@ export class EventsService { } } ], - _and: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}} + _and: ${filter} }}) { id location @@ -214,28 +344,6 @@ export class EventsService { created_by updated_by user_id - attendances { - context - context_id - created_by - date_time - id - lat - long - rsvp - status - updated_by - user_id - user { - first_name - id - last_name - middle_name - profile_url - aadhar_verified - aadhaar_verification_mode - } - } } }`, }; @@ -281,37 +389,6 @@ export class EventsService { type updated_by user_id - attendances(order_by: { - created_at: asc - }) { - created_by - created_at - context - context_id - date_time - id - lat - user_id - updated_by - status - long - rsvp - fa_is_processed - fa_similarity_percentage - user{ - first_name - id - last_name - middle_name - profile_url - aadhar_verified - aadhaar_verification_mode - program_faciltators{ - documents_status - } - } - } - } } `, @@ -337,9 +414,58 @@ export class EventsService { public async update(id: number, header: any, req: any, resp: any) { try { const userDetail = await this.userService.ipUserInfo(header); - let user_id = userDetail.data.id; - let attendees = req.attendees; - if (attendees && attendees.length > 0) { + const user_id = userDetail.data.id; + // Validate start date + const daysDiff = moment + .utc(req.end_date) + .diff(moment.utc(req.start_date), 'days'); + const currentDateTime = moment.utc(); + + const startDateTime = moment( + req.start_date + ' ' + req.start_time, + 'YYYY-MM-DD HH:mm', + true, // Parse in strict mode + ).utc(); + + let errorMessage = {}; + if (startDateTime.isBefore(currentDateTime, 'day')) { + errorMessage = { + key: 'start_date', + message: 'Start date cannot be a back date.', + }; + } else if (daysDiff < 0 || daysDiff > 5) { + errorMessage = { + key: 'event_days', + message: 'Event duration must be between 1 and 5 days.', + }; + } + if (Object.keys(errorMessage).length) { + return resp.status(422).send({ + success: false, + ...errorMessage, + data: {}, + }); + } + + // Convert start_date and end_date to UTC + const startDateTimeUTC = moment + .tz( + req.start_date + ' ' + req.start_time, + 'YYYY-MM-DD HH:mm', + 'Asia/Kolkata', + ) + .utc(); + + const endDateTimeUTC = moment + .tz( + req.end_date + ' ' + req.end_time, + 'YYYY-MM-DD HH:mm', + 'Asia/Kolkata', + ) + .utc(); + + const attendees = req.attendees; + if (attendees) { const data = { query: `query MyQuery { events(where: {id: {_eq: ${id}}}){ @@ -359,16 +485,15 @@ export class EventsService { data, ); let eventDetails = response?.data.events[0]; - let mappedData = response?.data.events.map( - (data) => data.attendances, - ); - if (response) { + if (eventDetails?.id) { + let mappedData = eventDetails?.attendances; //remove attendees in current event const deletePromise = []; - const deleteAttendees = mappedData[0].filter( - (data) => !req.attendees.includes(data.user_id), + const deleteAttendees = mappedData.filter( + (data) => !attendees.includes(data.user_id), ); if (deleteAttendees && deleteAttendees.length > 0) { + //remove for and add delete multiple query for (const iterator of deleteAttendees) { deletePromise.push( this.hasuraService.delete('attendance', { @@ -382,8 +507,8 @@ export class EventsService { } //add new attendees in current event - const tempArray = mappedData[0].map((data) => data.user_id); - const addAttendees = req.attendees.filter( + const tempArray = mappedData.map((data) => data.user_id); + const addAttendees = attendees.filter( (data) => !tempArray.includes(data), ); if (addAttendees && addAttendees.length > 0) { @@ -413,8 +538,13 @@ export class EventsService { } } //update events fields + const newRequest = { - ...req, + ...req, // Spread req object first + end_date: endDateTimeUTC.format('YYYY-MM-DD'), + end_time: endDateTimeUTC.format('HH:mm'), + start_time: startDateTimeUTC.format('HH:mm'), + start_date: startDateTimeUTC.format('YYYY-MM-DD'), ...(req.reminders && { reminders: JSON.stringify(req.reminders).replace( /"/g, @@ -483,6 +613,7 @@ export class EventsService { } public async updateAttendanceDetail(id: number, req: any, response: any) { + let attendance_id = id; const tableName = 'attendance'; if (req?.status == 'present') { let checkStringResult = this.checkStrings({}); @@ -496,6 +627,35 @@ export class EventsService { } } try { + const format = 'YYYY-MM-DD'; + const dateString = moment.utc().startOf('day').format(format); + const currentTime = moment.utc().format('HH:mm'); + + let data = { + query: `query MyQuery1 { + events_aggregate(where: {attendances: {id: {_eq: ${attendance_id}}}, start_date: {_lte: "${dateString}"}, end_date: {_gte: "${dateString}"}, start_time: {_lte: "${currentTime}"}, end_time: {_gte: "${currentTime}"}}) { + aggregate { + count + } + } + } + `, + }; + + const getAttendanceData = + await this.hasuraServiceFromServices.getData(data); + const count = + getAttendanceData?.data?.events_aggregate?.aggregate?.count; + + if (count === 0) { + return response.status(422).send({ + success: false, + message: + 'Attendance cannot be marked as today is not within the event date range.', + data: {}, + }); + } + let result = await this.hasuraService.update( +id, tableName, @@ -598,6 +758,7 @@ export class EventsService { }); } } + async getParticipants(req, id, body, res) { const auth_users = await this.userService.ipUserInfo(req, 'staff'); @@ -707,9 +868,93 @@ export class EventsService { public async createEventAttendance(body: any, req: any, res: any) { (body.status = body?.status || null), - (body.context = body?.context || 'events'), + (body.context = 'events'), (body.created_by = req?.mw_userid), (body.updated_by = req?.mw_userid); + const presentDate = body?.date_time; + //Taking event date and attendances count + let data = { + query: `query MyQuery { + events(where: {id: {_eq: ${body.context_id}}}){ + start_date + start_time + end_date + end_time + id + } + attendance_aggregate(where:{ + context_id:{_eq:${body?.context_id}}, + context:{_eq:${body.context}}, + date_time:{_gte:"${body?.date_time}",_lte:"${body?.date_time}"}, + user_id:{_eq:${body?.user_id}} + }){ + aggregate{ + count + } + } + } + `, + }; + + const getAttendanceData = await this.hasuraServiceFromServices.getData( + data, + ); + + let event = getAttendanceData?.data?.events?.[0]; + let count = + getAttendanceData?.data?.attendance_aggregate?.aggregate?.count; + //if attendances is present for that date + if (count > 0) { + return res.status(422).json({ + success: false, + message: 'Attendance allready marked for this date', + }); + } + + const format = 'YYYY-MM-DD HH:mm'; + const currentDate = moment(); + let errorMessage = {}; + const startDate = moment( + `${event?.start_date} ${event?.start_time}`, + format, + ); + + const endDate = moment(`${event?.end_date} ${event?.end_time}`, format); + + const newPresentDate = moment( + `${presentDate} ${moment().format('HH:mm')}`, + format, + ); + //current date is after start date of event and presnt date is after event start date + if ( + startDate.isSameOrAfter(currentDate) || + startDate.isSameOrAfter(newPresentDate) + ) { + errorMessage = { + key: 'date_time', + message: 'ATTENDANCE_FUTURE_DATE_ERROR_MESSAGE', + }; + } else if ( + endDate.isSameOrBefore(currentDate) || + endDate.isSameOrBefore(newPresentDate) + ) { + errorMessage = { + key: 'date_time', + message: 'ATTENDANCE_PAST_DATE_ERROR_MESSAGE', + }; + } else if (!newPresentDate.isSameOrBefore(currentDate)) { + errorMessage = { + key: 'date_time', + message: 'ATTENDANCE_FUTURE_DATE_ERROR_MESSAGE', + }; + } + + if (Object.keys(errorMessage).length > 0) { + return res.status(422).json({ + success: false, + ...errorMessage, + }); + } let response = await this.hasuraService.q( 'attendance', @@ -767,34 +1012,28 @@ export class EventsService { events(where: {end_date:{_gte:"${todayDate}"},academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}},attendances: {context: {_eq: ${context}}, user_id: {_eq: ${id}}}}, limit: $limit, offset: $offset) { id user_id - context - context_id - created_by - updated_by - created_at - updated_at start_date start_time end_date end_time name - location - location_type type params master_trainer lms_test_tracking(where: {user_id: {_eq: ${id}},context:{_eq:${context}}}) { - context - context_id status - created_at - updated_at id test_id score user_id certificate_status } + attendances(where: {context: {_eq: ${context}}, user_id: {_eq: ${id}}}){ + id + user_id + status + date_time + } } }`, diff --git a/src/src/facilitator/facilitator.module.ts b/src/src/facilitator/facilitator.module.ts index b99baf1c7..1cdbb0112 100644 --- a/src/src/facilitator/facilitator.module.ts +++ b/src/src/facilitator/facilitator.module.ts @@ -19,6 +19,7 @@ import { FacilitatorCoreService } from './facilitator.core.service'; import { FacilitatorService } from './facilitator.service'; import { CohortMiddleware } from 'src/common/middlewares/cohort.middleware'; import { Method } from '../common/method/method'; +import { IpMiddleware } from 'src/common/middlewares/ip.middleware'; @Module({ imports: [ @@ -37,6 +38,7 @@ import { Method } from '../common/method/method'; export class FacilitatorModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(AuthMiddleware).forRoutes('*'); + consumer.apply(IpMiddleware).forRoutes('/facilitators'); consumer .apply(CohortMiddleware) .exclude( diff --git a/src/src/facilitator/facilitator.service.ts b/src/src/facilitator/facilitator.service.ts index b80a1944b..fedbc6b72 100644 --- a/src/src/facilitator/facilitator.service.ts +++ b/src/src/facilitator/facilitator.service.ts @@ -74,13 +74,79 @@ export class FacilitatorService { body: any, response: any, ) { - const user = await this.userService.ipUserInfo(request); const program_id = request.mw_program_id; const academic_year_id = request.mw_academic_year_id; + const user = await this.userService.getIpRoleUserById( + request.mw_userid, + { program_id, academic_year_id }, + ); + const page = isNaN(body.page) ? 1 : parseInt(body.page); const limit = isNaN(body.limit) ? 15 : parseInt(body.limit); let skip = page > 1 ? limit * (page - 1) : 0; + let filterQueryArray = []; + + let status; + switch (body?.type) { + case 'pragati_orientation': { + status = `status: { _in: ["applied" ]}`; + break; + } + case 'pcr_training': { + status = `status: { _in: ["pragati_mobilizer","selected_for_onboarding" ]}`; + break; + } + case 'main_camp_execution_training': { + status = `status: { _in: ["selected_for_onboarding" ]}`; + break; + } + case 'drip_training': { + status = `status: { _in: ["applied","pragati_mobilizer","selected_for_onboarding" ]}`; + break; + } + } + + filterQueryArray.push( + ` program_faciltators: { + parent_ip: { _eq: "${user?.program_users[0]?.organisation_id}" },academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}}, + ${status}}`, + ); + + if (body.search && body.search !== '') { + let first_name = body.search.split(' ')[0]; + let last_name = body.search.split(' ')[1] || ''; + + if (last_name?.length > 0) { + filterQueryArray.push(` + first_name: { _ilike: "%${first_name}%" }, + last_name: { _ilike: "%${last_name}%" } + `); + } else { + filterQueryArray.push( + `first_name: { _ilike: "%${first_name}%" }`, + ); + } + } + + if (body?.district && body?.district.length > 0) { + filterQueryArray.push( + `district:{_in: ${JSON.stringify(body?.district)}}`, + ); + } + + if (body?.block && body?.block.length > 0) { + filterQueryArray.push( + `block:{_in: ${JSON.stringify(body?.block)}}`, + ); + } + if (body?.village && body?.village.length > 0) { + filterQueryArray.push( + `village:{_in: ${JSON.stringify(body?.village)}}`, + ); + } + + let filterQuery = '' + filterQueryArray.join(',') + ''; const data = { query: ` @@ -89,19 +155,8 @@ export class FacilitatorService { where: { _and: [ { - program_faciltators: { - parent_ip: { _eq: "${user?.data?.program_users[0]?.organisation_id}" },academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}}, - status: { _eq: "shortlisted_for_orientation" } - } + ${filterQuery} }, - { - attendances_aggregate: { - count: { - predicate: {_eq: 0}, - filter: {event: {type: {_eq: "${body.type}"}}} - } - } - } ] } ) { @@ -114,19 +169,8 @@ export class FacilitatorService { where: { _and: [ { - program_faciltators: { - parent_ip: { _eq: "${user?.data?.program_users[0]?.organisation_id}" },academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}}, - status: { _eq: "shortlisted_for_orientation" } - } + ${filterQuery} }, - { - attendances_aggregate: { - count: { - predicate: {_eq: 0}, - filter: {event: {type: {_eq: "${body.type}"}}} - } - } - } ] }, limit: $limit, @@ -137,138 +181,18 @@ export class FacilitatorService { first_name middle_name last_name - dob - aadhar_token - address - block_id - block_village_id - created_by - district_id - email_id gender - lat - long mobile - state_id - updated_by - profile_url state district block village grampanchayat - program_users { - id - organisation_id - academic_year_id - program_id - role_id - status - user_id - } - core_faciltator { - created_by - device_ownership - device_type - id - pan_no - refreere - sourcing_channel - updated_by - user_id - } - experience { - description - end_year - experience_in_years - institution - start_year - organization - role_title - user_id - type - } - program_faciltators { - parent_ip - availability - has_social_work_exp - id - police_verification_done - program_id - social_background_verified_by_neighbours - user_id - village_knowledge_test - status - form_step_number - created_by - updated_by - academic_year_id - } - qualifications { - created_by - end_year - id - institution - qualification_master_id - start_year - updated_by - user_id - qualification_master { - context - context_id - created_by - id - name - type - updated_by - } - } - interviews { + program_faciltators(where:{academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}}}) { id - title user_id - owner_user_id - date - start_time - end_time - interviewer_name status - comment - reminder - rsvp - location_type - location - created_at - created_by - updated_at - updated_by - owner { - first_name - last_name - id - } - } - events { - context - context_id - created_by - end_date - end_time - id - location - location_type - start_date - start_time - updated_by - user_id - } - documents(order_by: {id: desc}){ - id - user_id - name - doument_type - document_sub_type - } + } } } `, @@ -282,12 +206,6 @@ export class FacilitatorService { let usersList = hasuraResponse?.data?.users; - usersList = usersList.map((obj) => { - obj.program_faciltators = obj.program_faciltators?.[0] || {}; - obj.qualifications = obj.qualifications?.[0] || {}; - return obj; - }); - const count = hasuraResponse?.data?.users_aggregate?.aggregate?.count || 0; @@ -1645,18 +1563,30 @@ export class FacilitatorService { } async getFacilitators(req: any, body: any, resp: any) { - const user: any = await this.userService.ipUserInfo(req); const academic_year_id = req.mw_academic_year_id; const program_id = req.mw_program_id; - if (!user?.data?.program_users?.[0]?.organisation_id) { - return resp.status(400).send({ + + if (req.mw_roles?.includes('program_owner')) { + req.parent_ip_id = req.mw_ip_user_id; + } else { + const user = await this.userService.ipUserInfo(req); + if (req.mw_roles?.includes('staff')) { + req.parent_ip_id = + user?.data?.program_users?.[0]?.organisation_id; + } else if (req.mw_roles?.includes('facilitator')) { + req.parent_ip_id = user?.data?.program_faciltators?.parent_ip; + } + } + if (!req.parent_ip_id) { + return resp.status(404).send({ success: false, - message: 'Invalid User', + message: 'Invalid Ip', data: {}, }); } + const page = isNaN(body.page) ? 1 : parseInt(body.page); - const limit = isNaN(body.limit) ? 15 : parseInt(body.limit); + const limit = isNaN(body.limit) ? 10 : parseInt(body.limit); let skip = page > 1 ? limit * (page - 1) : 0; @@ -1726,7 +1656,7 @@ export class FacilitatorService { } filterQueryArray.unshift( - `{program_faciltators: {id: {_is_null: false}, parent_ip: {_eq: "${user?.data?.program_users[0]?.organisation_id}"}, academic_year_id: {_eq: ${academic_year_id}},program_id:{_eq:${program_id}}}}`, + `{program_faciltators: {id: {_is_null: false}, parent_ip: {_eq: "${req.parent_ip_id}"}, academic_year_id: {_eq: ${academic_year_id}},program_id:{_eq:${program_id}}}}`, ); let filterQuery = '{ _and: [' + filterQueryArray.join(',') + '] }'; @@ -2602,6 +2532,7 @@ export class FacilitatorService { academic_year_id: academic_year_id, parent_ip: parent_ip, program_id: program_id, + status: 'applied', qualification_ids: JSON.stringify( JSON.parse(otherData.qualification_ids), ).replace(/"/g, '\\"'), diff --git a/src/src/helper/queryGenerator.service.ts b/src/src/helper/queryGenerator.service.ts index d3998554e..146eb4ffa 100644 --- a/src/src/helper/queryGenerator.service.ts +++ b/src/src/helper/queryGenerator.service.ts @@ -20,6 +20,17 @@ export class QueryGeneratorService { return str; }; + filterObjectByKeyArray = (obj: any, desiredKeys: []) => { + const filteredObject = desiredKeys.reduce((acc: any, key) => { + if (key in obj) { + acc[key] = obj[key]; + } + return acc; + }, {}); + + return filteredObject; + }; + // create create(tName: String, item: any, onlyFields: any = [], fields: any = []) { let tableName = `insert_${tName}_one`; @@ -337,8 +348,13 @@ export class QueryGeneratorService { onlyFields: any = [], request: any = { filters: {}, page: '0', limit: '0' }, ) { - const getObjStr = (request: any) => { - const { filters, page, limit, order_by } = request; + const getObjStr = (request: any, is_aggregate = false) => { + const { filter, page, limit, order_by, onlyfilter } = request; + const filters = this.filterObjectByKeyArray( + filter || {}, + onlyfilter || [], + ); + let str = ''; if ( (limit && limit != '0') || @@ -347,26 +363,31 @@ export class QueryGeneratorService { ) { str += '('; let paramArr = []; + if (filters && Object.keys(filters).length > 0) { let filterStr = `where: {`; let strArr = Object.keys(filters).map((e) => { - if (this.isEmptyObject(filters[e])) { + let qData = ''; + if (e === 'core') { + qData = filters[e]; + } else if (this.isEmptyObject(filters[e])) { let data = this.objectConvert( filters[e], ([key, val]) => { return `${key}: "${val}"`; }, ); - return `${e}:{${data.join(',')}}`; + qData = `${e}:{${data.join(',')}}`; } else if (filters && filters[e] != '') { - return `${e}:{_eq:"${filters[e]}"}`; + qData = `${e}:{_eq:"${filters[e]}"}`; } + return qData; }); filterStr += strArr.join(); filterStr += `}`; paramArr = [...paramArr, filterStr]; } - if (limit) { + if (limit && !is_aggregate) { let offset = 0; if (page > 1 && limit) { offset = parseInt(limit) * (page - 1); @@ -387,9 +408,8 @@ export class QueryGeneratorService { } return str; }; - - return `query MyQuery { - ${tableName}_aggregate${getObjStr(request)} { + const query = `query MyQuery { + ${tableName}_aggregate${getObjStr(request, true)} { aggregate { count } @@ -399,6 +419,8 @@ export class QueryGeneratorService { } } `; + + return query; } findOne(id: number, tName: String, onlyFields: any = []) { diff --git a/src/src/lms/certificate_html.ts b/src/src/lms/certificate_html.ts index aa1015e02..88e60000c 100644 --- a/src/src/lms/certificate_html.ts +++ b/src/src/lms/certificate_html.ts @@ -5,219 +5,350 @@ export const html_code = ` Certificate - - -
-
+ + +
+
-
+
-
-
-
यह प्रमाणित किया जाता है कि
+
+
+ +
+
+
+
सत्र   
+
{{academic_year}}
+
 के लिए
+
+
+
+
यह प्रमाणित किया जाता है कि
-
श्री / सुश्री   
{{name}}
-
ने प्रोजेक्ट प्रगति शिविर के संचालन हेतु आयोजित प्रशिक्षण में पूरे उत्साह और प्रतिबद्धता से
भाग लेकर आवश्यक ज्ञान एवं कौशल अर्जित किया है|
-
आपको प्रगति शिविर संचालन के लिए प्रेरक के रूप में प्रमाणित करते हुए हमें असीम हर्ष का अनुभव हो रहा है
-
प्रशिक्षण में अर्जित किए गए कौशल: -
-
-
- सीखने के सत्रों को सुगम बनाना +
+
श्री / सुश्री   
+
{{name}}
+
 ने
+
+
+
दिनांक :   
+
+ {{event_start_date}} +
+
 से 
+
+ {{event_end_date}}
-
- समस्या - समाधान +
 तक
+
+
+
+ 'प्रोजेक्ट प्रगति' के प्रशिक्षण को सफलतापूर्वक पूरा + किया है।
- सीखने में तकनीकी प्रयोग + इस प्रशिक्षण के दौरान आपने शिक्षा से वंचित शिक्षार्थियों की + पहचान, उनका मार्गदर्शन, दस्तावेजीकरण और
- -
- -
-
- पारस्परिक संबंध कौशल +
+ ओपन स्कूल के माध्यम से 10 वीं कक्षा में नामांकन की प्रक्रिया + के बारे में जानकारी प्राप्त की है।
-
- सामुदायिक प्रबंधन +
+ आपको 'प्रगति मोबिलाइजर' के रूप में प्रमाणित करते हुए + हमें असीम हर्ष का अनुभव हो रहा है। +
+
+
+
+ प्रशिक्षण में अर्जित किए गए कौशल: +
+
+
+
+
+ + समुदाय के साथ प्रभावी संवाद +
+
+ + शिक्षा से वंचित शिक्षार्थियों को मार्गदर्शन प्रदान करना +
-
- -
-
-
-
 
-
तिथि :  
{{issue_date}}
-
UID :  
{{user_id}}
-
-
- +
+
+ + समस्या समाधान +
+
+ + 10 वीं के नामांकन के लिए दस्तावेजीकरण की समझ +
+
+
-
- -
Safeena Hussain
-
(Founder and Board Member)
-
- -
-
-
-
- -
- +
+
+
 
+
+
दिनांक :  
+
+ {{issue_date}} +
+
+
+
UID :  
+
+ {{user_id}} +
+
+
+ * + यह प्रमाणपत्र सिर्फ प्रशिक्षण में भागीदारी के लिए दिया जा रहा + है। +
+
+
+ +
+
+
+ +
+
गीतिका टंडन हिगिंस
+
(एसोसिएट निदेशक, प्रोजेक्ट प्रगति)
+
+
+
+ +
+
-
- + `; diff --git a/src/src/main.ts b/src/src/main.ts index 52cd72b3f..63f74e7af 100755 --- a/src/src/main.ts +++ b/src/src/main.ts @@ -5,7 +5,6 @@ const Sentry = require('@sentry/node'); async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); - Sentry.init({ dsn: process.env.SENTRY_DSN_URL, environment: process.env.SENTRY_ENVIRONMENT, diff --git a/src/src/modules/auth/auth.module.ts b/src/src/modules/auth/auth.module.ts index e1d89282e..52cdc5702 100644 --- a/src/src/modules/auth/auth.module.ts +++ b/src/src/modules/auth/auth.module.ts @@ -7,6 +7,8 @@ import { KeycloakModule } from 'src/services/keycloak/keycloak.module'; import { UserModule } from 'src/user/user.module'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; +import { Method } from 'src/common/method/method'; +import { CohortMiddleware } from 'src/common/middlewares/cohort.middleware'; @Module({ imports: [ @@ -17,11 +19,14 @@ import { AuthService } from './auth.service'; UserModule, ], controllers: [AuthController], - providers: [AuthService], + providers: [AuthService, Method], exports: [AuthService], }) export class AuthModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(AuthMiddleware).forRoutes('*'); + consumer + .apply(CohortMiddleware) + .forRoutes('/auth/reset-password-admin'); } } diff --git a/src/src/modules/auth/auth.service.ts b/src/src/modules/auth/auth.service.ts index a90dc6030..b122b9c1d 100644 --- a/src/src/modules/auth/auth.service.ts +++ b/src/src/modules/auth/auth.service.ts @@ -256,11 +256,11 @@ export class AuthService { } public async resetPasswordUsingId(req, header, response) { - console.log('req', req); + const { mw_roles } = header; const authToken = header.header('authorization'); const decoded: any = jwt_decode(authToken); let keycloak_id = decoded.sub; - console.log('keycloak_id', keycloak_id); + let query2 = { query: `query MyQuery { users(where: {keycloak_id: {_eq: "${keycloak_id}" }}) { @@ -276,12 +276,9 @@ export class AuthService { } }`, }; + const userRole = await this.hasuraService.postData(query2); - console.log( - 'userRole', - userRole.data.users[0].program_users[0].roles.role_type, - ); - if (userRole.data.users[0].program_users[0].roles.role_type === 'IP') { + if (mw_roles.includes('staff')) { let query = { query: `query MyQuery { users_by_pk(id: ${req.id}) { @@ -294,7 +291,6 @@ export class AuthService { }; const userRes = await this.hasuraService.postData(query); - console.log('userRes', userRes); if (userRes) { const token = @@ -500,6 +496,15 @@ export class AuthService { ) { misssingFieldsFlag = true; } + } else if (body.role === 'staff') { + if ( + !body.role_fields.organisation_id || + !body.role_fields.program_id || + !body.role_fields.academic_year_id || + !body.role_fields.role_slug + ) { + misssingFieldsFlag = true; + } } else { misssingFieldsFlag = true; } @@ -534,6 +539,10 @@ export class AuthService { group = `beneficiaries`; break; } + case 'staff': { + group = `staff`; + break; + } } let data_to_create_user = { @@ -613,6 +622,12 @@ export class AuthService { if (body.role_fields.facilitator_id) { body.facilitator_id = body.role_fields.facilitator_id; } + if (body.role_fields.organisation_id) { + body.organisation_id = body.role_fields.organisation_id; + } + if (body.role_fields.role_slug) { + body.role_slug = body.role_fields.role_slug; + } if (body.role === 'facilitator' && body.hasOwnProperty('dob')) { delete body.dob; } @@ -640,6 +655,8 @@ export class AuthService { ['status', 'reason_for_status_update'], ); } + if (body.role === 'staff' && result.data.program_users) { + } // Send login details SMS // नमस्कार, प्रगति प्लेटफॉर्म पर आपका अकाउंट बनाया गया है। आपका उपयोगकर्ता नाम है और पासवर्ड है। FEGG if (body.role === 'facilitator') { @@ -794,6 +811,14 @@ export class AuthService { 'keycloak_id', 'username', 'aadhar_verified', + 'village', + 'state', + 'block', + 'district', + 'grampanchayat', + 'pincode', + 'lat', + 'long', ], ); @@ -821,6 +846,15 @@ export class AuthService { req.academic_year_id = req.role_fields.academic_year_id; req.status = 'applied'; } + if (req.role === 'staff') { + programRoleTableName = 'program_users'; + groupId = 'organisation_id'; + req.organisation_id = `${req.role_fields.organisation_id}`; + req.program_id = req.role_fields.program_id; + req.academic_year_id = req.role_fields.academic_year_id; + req.role_slug = req.role_fields.role_slug; + other = [...other, 'role_slug']; + } console.log('tableName', programRoleTableName); console.log('groupId', groupId); diff --git a/src/src/modules/housekeeping/housekeeping.controller.ts b/src/src/modules/housekeeping/housekeeping.controller.ts new file mode 100644 index 000000000..4e4ebaabe --- /dev/null +++ b/src/src/modules/housekeeping/housekeeping.controller.ts @@ -0,0 +1,15 @@ +import { HouseKeepingService } from './housekeeping.service'; +//import { Body, Controller, Post, UseGuards } from '@nestjs/common'; +//import { AuthGuard } from '../auth/auth.guard'; + +//@Controller('hk') +export class HouseKeepingController { + constructor(private houseKeepingService: HouseKeepingService) {} + /* + @Post('/download-files') + //@UseGuards(new AuthGuard()) + public async downloadFiles(@Body() body) { + await this.houseKeepingService.downloadFiles(body); + } + */ +} diff --git a/src/src/modules/housekeeping/housekeeping.module.ts b/src/src/modules/housekeeping/housekeeping.module.ts new file mode 100644 index 000000000..3842d91d4 --- /dev/null +++ b/src/src/modules/housekeeping/housekeeping.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { HouseKeepingService } from './housekeeping.service'; +import { HouseKeepingController } from './housekeeping.controller'; + +@Module({ + providers: [HouseKeepingService], + controllers: [HouseKeepingController], +}) +export class HouseKeepingModule {} diff --git a/src/src/modules/housekeeping/housekeeping.service.ts b/src/src/modules/housekeeping/housekeeping.service.ts new file mode 100644 index 000000000..300e86de3 --- /dev/null +++ b/src/src/modules/housekeeping/housekeeping.service.ts @@ -0,0 +1,38 @@ +import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; +const fs = require('fs'); +export class HouseKeepingService { + async downloadFiles(body) { + const bucketName = process.env.S3_BUCKET; + const fileNames = body.fileNames; + const destinationFolder = body.destinationFolder; + + const s3 = new S3Client({ + region: process.env.S3_REGION, + credentials: { + secretAccessKey: process.env.SECRET_ACCESS_KEY, + accessKeyId: process.env.ACCESS_KEY_ID, + }, + }); + + try { + for (const fileName of fileNames) { + const getObjectCommand = new GetObjectCommand({ + Bucket: bucketName, + Key: fileName, + }); + + const getObjectCommandResponse = await s3.send( + getObjectCommand, + ); + + // 2 - Write image from S3 to temporary file + fs.writeFileSync( + destinationFolder + '/' + fileName, + await getObjectCommandResponse.Body.transformToByteArray(), + ); + } + } catch (error) { + console.error('Error:', error); + } + } +} diff --git a/src/src/observations/dto/field-responses-search.dto.ts b/src/src/observations/dto/field-responses-search.dto.ts new file mode 100644 index 000000000..71961e308 --- /dev/null +++ b/src/src/observations/dto/field-responses-search.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString, IsObject } from 'class-validator'; + +export class FieldResponsesSearchDto { + @IsNotEmpty() + @IsObject() + filters: any; +} diff --git a/src/src/observations/dto/field-responses.dto.ts b/src/src/observations/dto/field-responses.dto.ts new file mode 100644 index 000000000..ebc6c7c62 --- /dev/null +++ b/src/src/observations/dto/field-responses.dto.ts @@ -0,0 +1,23 @@ +import { IsNotEmpty, IsString, IsObject, IsNumber } from 'class-validator'; + +export class FieldResponsesDto { + @IsNotEmpty() + @IsNumber() + observation_id: number; + + @IsNotEmpty() + @IsNumber() + field_id: number; + + @IsNotEmpty() + @IsString() + response_value: string; + + @IsNotEmpty() + @IsString() + context: string; + + @IsNotEmpty() + @IsNumber() + context_id: number; +} diff --git a/src/src/observations/dto/field-search.dto.ts b/src/src/observations/dto/field-search.dto.ts new file mode 100644 index 000000000..703c50d2d --- /dev/null +++ b/src/src/observations/dto/field-search.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString, IsObject } from 'class-validator'; + +export class FieldSearchDto { + @IsNotEmpty() + @IsObject() + filters: any; +} diff --git a/src/src/observations/dto/field.dto.ts b/src/src/observations/dto/field.dto.ts new file mode 100644 index 000000000..c80c4a17c --- /dev/null +++ b/src/src/observations/dto/field.dto.ts @@ -0,0 +1,15 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class FieldDto { + @IsNotEmpty() + @IsString() + name: string; + + @IsNotEmpty() + @IsString() + data_type: string; + + @IsNotEmpty() + @IsString() + title: string; +} diff --git a/src/src/observations/dto/observation-fields-search.dto.ts b/src/src/observations/dto/observation-fields-search.dto.ts new file mode 100644 index 000000000..cba2d1d62 --- /dev/null +++ b/src/src/observations/dto/observation-fields-search.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString, IsObject } from 'class-validator'; + +export class ObservationFieldSearchDto { + @IsNotEmpty() + @IsObject() + filters: any; +} diff --git a/src/src/observations/dto/observation-fields.dto.ts b/src/src/observations/dto/observation-fields.dto.ts new file mode 100644 index 000000000..b4d5430cc --- /dev/null +++ b/src/src/observations/dto/observation-fields.dto.ts @@ -0,0 +1,23 @@ +import { IsNotEmpty, IsString, IsNumber, IsObject } from 'class-validator'; + +export class ObservationFieldsDto { + @IsNotEmpty() + @IsNumber() + observation_id: number; + + @IsNotEmpty() + @IsString() + context: string; + + @IsNotEmpty() + @IsNumber() + fields_sequence: number; + + @IsNotEmpty() + @IsNumber() + context_id: number; + + @IsNotEmpty() + @IsNumber() + field_id: number; +} diff --git a/src/src/observations/dto/observation-search.dto.ts b/src/src/observations/dto/observation-search.dto.ts new file mode 100644 index 000000000..fd2f51ac7 --- /dev/null +++ b/src/src/observations/dto/observation-search.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString, IsObject } from 'class-validator'; + +export class ObservationSearchDto { + @IsNotEmpty() + @IsObject() + filters: any; +} diff --git a/src/src/observations/dto/observation.dto.ts b/src/src/observations/dto/observation.dto.ts new file mode 100644 index 000000000..06629acc9 --- /dev/null +++ b/src/src/observations/dto/observation.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class ObservationDto { + @IsNotEmpty() + @IsString() + name: string; +} diff --git a/src/src/observations/observations.controller.spec.ts b/src/src/observations/observations.controller.spec.ts new file mode 100644 index 000000000..54535557c --- /dev/null +++ b/src/src/observations/observations.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ObservationsController } from './observations.controller'; + +describe('ObservationsController', () => { + let controller: ObservationsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ObservationsController], + }).compile(); + + controller = module.get(ObservationsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/src/observations/observations.controller.ts b/src/src/observations/observations.controller.ts new file mode 100644 index 000000000..271ec5377 --- /dev/null +++ b/src/src/observations/observations.controller.ts @@ -0,0 +1,429 @@ +import { + Body, + Controller, + Get, + Param, + Post, + Req, + Res, + UseGuards, + UsePipes, + ValidationPipe, + Response, + Request, + Patch, + Delete, +} from '@nestjs/common'; +import { AuthGuard } from 'src/modules/auth/auth.guard'; +import { ObservationsService } from './observations.service'; +import { ObservationDto } from './dto/observation.dto'; +import { FieldDto } from './dto/field.dto'; +import { FieldSearchDto } from './dto/field-search.dto'; +import { ObservationSearchDto } from './dto/observation-search.dto'; +import { ObservationFieldsDto } from './dto/observation-fields.dto'; +import { ObservationFieldSearchDto } from './dto/observation-fields-search.dto'; +import { FieldResponsesDto } from './dto/field-responses.dto'; +import { FieldResponsesSearchDto } from './dto/field-responses-search.dto'; + +@Controller('observations') +export class ObservationsController { + constructor(public observationsService: ObservationsService) {} + + @Post('/') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async createObservation( + @Body() body: ObservationDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.createObservation( + body, + response, + request, + ); + } + + @Post('/observation-fields') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async createObservationFields( + @Body() body: ObservationFieldsDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.createObservationFields( + body, + response, + request, + ); + } + + @Patch('/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async updateObservation( + @Body() body: ObservationDto, + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.updateObservation( + body, + response, + request, + id, + ); + } + + @Patch('/observation-fields/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async updateObservationField( + @Body() body: ObservationFieldsDto, + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.updateObservationField( + body, + response, + request, + id, + ); + } + + @Get('/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationList( + @Body() body: Body, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getObservationList( + body, + response, + request, + ); + } + + @Get('/observation-fields/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationFieldList( + @Body() body: Body, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getObservationFieldList( + body, + response, + request, + ); + } + + @Get('/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.getObservationById( + response, + request, + id, + ); + } + + @Get('/observation-fields/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationFieldById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.getObservationFieldById( + response, + request, + id, + ); + } + + @Delete('/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async deleteObservationById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.deleteObservationById( + response, + request, + id, + ); + } + + @Delete('observation-fields/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async deleteObservationFieldById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.deleteObservationFieldById( + response, + request, + id, + ); + } + + @Post('/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationListByName( + @Body() body: ObservationSearchDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getObservationList( + body, + response, + request, + ); + } + + @Post('/list/type/:type') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationListByType( + @Body() body: ObservationSearchDto, + @Res() response: Response, + @Req() request: Request, + @Param('type') type: string, + ) { + return this.observationsService.getObservationByType( + body, + response, + request, + type, + ); + } + + @Post('/report') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationReport( + @Body() body: ObservationSearchDto, + @Res() response: Response, + @Req() request: Request, + @Param('type') type: string, + ) { + return this.observationsService.getObservationReport( + body, + response, + request, + ); + } + + @Post('observation-fields/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getObservationFieldsListByName( + @Body() body: ObservationFieldSearchDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getObservationFieldList( + body, + response, + request, + ); + } + + @Post('/fields') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async createFields( + @Body() body: FieldDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.createFields(body, response, request); + } + + @Patch('/fields/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async updateFields( + @Body() body: FieldDto, + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.updateFields( + body, + response, + request, + id, + ); + } + + @Get('/fields/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getFieldsList( + @Body() body: Body, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getFieldsList(body, response, request); + } + + @Get('/fields/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getFieldById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.getFieldById(response, request, id); + } + + @Delete('/fields/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async deleteFieldById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.deleteFieldById(response, request, id); + } + + @Post('/fields/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getFieldListByName( + @Body() body: FieldSearchDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getFieldsList(body, response, request); + } + + // + + @Post('/field-responses') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async createFieldResponses( + @Body() body: FieldResponsesDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.createFieldResponses( + body, + response, + request, + ); + } + + @Post('/field-responses/bulk') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async createFieldResponsesMany( + @Body() body: Body, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.createFieldResponsesMany( + body, + response, + request, + ); + } + + @Patch('/field-responses/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async updateFieldResponses( + @Body() body: FieldResponsesDto, + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.updateFieldResponses( + body, + response, + request, + id, + ); + } + + @Get('/field-responses/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getFieldResponsesList( + @Body() body: Body, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getFieldResponsesList( + body, + response, + request, + ); + } + + @Get('/field-responses/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getFieldResponsesById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.getFieldResponsesById( + response, + request, + id, + ); + } + + @Delete('/field-responses/:id') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async deleteFieldResponsesById( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.observationsService.deleteFieldResponsesById( + response, + request, + id, + ); + } + + @Post('/field-responses/list') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + public async getFieldResponsesListByName( + @Body() body: FieldResponsesSearchDto, + @Res() response: Response, + @Req() request: Request, + ) { + return this.observationsService.getFieldResponsesList( + body, + response, + request, + ); + } +} diff --git a/src/src/observations/observations.module.ts b/src/src/observations/observations.module.ts new file mode 100644 index 000000000..871795314 --- /dev/null +++ b/src/src/observations/observations.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { HasuraModule } from 'src/hasura/hasura.module'; +import { UserModule } from 'src/user/user.module'; +import { HasuraModule as HasuraModuleFromServices } from '../services/hasura/hasura.module'; +import { ObservationsService } from './observations.service'; +import { ObservationsController } from './observations.controller'; +import { Method } from 'src/common/method/method'; + +@Module({ + imports: [HasuraModule, HasuraModuleFromServices, UserModule], + providers: [ObservationsService, Method], + controllers: [ObservationsController], +}) +export class ObservationsModule {} diff --git a/src/src/observations/observations.service.spec.ts b/src/src/observations/observations.service.spec.ts new file mode 100644 index 000000000..c6322f614 --- /dev/null +++ b/src/src/observations/observations.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ObservationsService } from './observations.service'; + +describe('ObservationsService', () => { + let service: ObservationsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ObservationsService], + }).compile(); + + service = module.get(ObservationsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/src/observations/observations.service.ts b/src/src/observations/observations.service.ts new file mode 100644 index 000000000..ed00cd199 --- /dev/null +++ b/src/src/observations/observations.service.ts @@ -0,0 +1,2038 @@ +import { Injectable } from '@nestjs/common'; +import { HasuraService } from 'src/hasura/hasura.service'; +import { UserService } from 'src/user/user.service'; +import { HasuraService as HasuraServiceFromServices } from '../services/hasura/hasura.service'; + +@Injectable() +export class ObservationsService { + public tableName = 'observations'; + public fillable = [ + 'name', + 'created_by', + 'created_at', + 'updated_at', + 'updated_by', + ]; + public returnFields = [ + 'id', + 'name', + 'created_by', + 'created_at', + 'updated_at', + 'updated_by', + ]; + + public FieldTableName = 'fields'; + public FieldFillable = [ + 'name', + 'created_by', + 'created_at', + 'updated_at', + 'updated_by', + 'data_type', + 'description', + 'extra_all_info', + 'title', + 'enum', + ]; + public FieldReturnFields = [ + 'id', + 'name', + 'created_by', + 'created_at', + 'updated_at', + 'updated_by', + 'data_type', + 'description', + 'extra_all_info', + 'title', + 'enum', + ]; + constructor( + private readonly hasuraService: HasuraService, + private hasuraServiceFromServices: HasuraServiceFromServices, + private userService: UserService, + ) {} + + async createObservation(body: any, resp: any, request: any) { + let user_id = request?.mw_userid; + let response; + let data; + body.created_by = user_id; + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + //check for duplicate name validation + + let vquery = `query MyQuery { + observations_aggregate(where: {name: {_eq:"${body?.name}"}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = + vresponse?.data?.observations_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate name encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + data = { + query: `mutation CreateObservations { + insert_observations_one(object: {${query}}) { + id + name + } + } + `, + variables: {}, + }; + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + let result = response?.data?.data?.insert_observations_one; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Observation created successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to create obsevation !', + data: {}, + }); + } + } + + async createFields(body: any, resp: any, request: any) { + let data; + let user_id = request?.mw_userid; + + body.created_by = user_id; + body.updated_by = user_id; + + body.enum = body?.enum + ? JSON.stringify(body?.enum).replace(/"/g, '\\"') + : ''; + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let vquery = `query MyQuery { + fields_aggregate(where: {title: {_eq:"${body?.title}"}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = vresponse?.data?.fields_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate title encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + data = { + query: `mutation CreateFields { + insert_fields_one(object: {${query}}) { + id + name + title + enum + + + } + } + `, + variables: {}, + }; + + let response = await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + let result = response?.data?.data?.insert_fields_one; + if (result) { + return resp.status(200).json({ + success: true, + message: 'Field created successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to create Field !', + data: {}, + }); + } + } + + async createObservationFields(body: any, resp: any, request: any) { + let user_id = request?.mw_userid; + let response; + let data; + body.created_by = user_id; + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let vquery = `query MyQuery { + observation_fields_aggregate(where: {observation_id: {_eq:${body?.observation_id}}, field_id: {_eq:${body?.field_id}}, context_id: {_eq:${body?.context_id}}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = + vresponse?.data?.observation_fields_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate observation field encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + data = { + query: `mutation CreateObservationFields { + insert_observation_fields_one(object: {${query}}) { + id + observation_id + context + context_id + field_id + } + } + `, + variables: {}, + }; + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + let result = response?.data?.data?.insert_observation_fields_one; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Observation Field created successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to create obsevation Field!', + data: {}, + }); + } + } + + async createFieldResponses(body: any, resp: any, request: any) { + let user_id = request?.mw_userid; + let response; + let data; + body.created_by = user_id; + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let vquery = `query MyQuery { + field_responses_aggregate(where: {context_id: {_eq:${body?.context_id}},observation_fields_id:{_eq:${body?.observation_fields_id}}, field_id: {_eq:${body?.field_id}}, observation_id: {_eq:${body?.observation_id}}, response_value: {_eq:"${body?.response_value}"}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = + vresponse?.data?.field_responses_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate field responses encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + data = { + query: `mutation CreateFieldsResponses { + insert_field_responses_one(object: {${query}}) { + id + observation_id + context + context_id + response_value + } + } + `, + variables: {}, + }; + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + let result = response?.data?.data?.insert_field_responses_one; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Observation Field created successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to create obsevation Field!', + data: {}, + }); + } + } + + async updateObservation(body: any, resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let vquery = `query MyQuery { + observations_aggregate(where: {name: {_eq:"${body?.name}"}, id: {_neq:${id}}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = + vresponse?.data?.observations_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate name encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + let data = { + query: ` + mutation UpdateObservations($id:Int!) { + update_observations_by_pk( + pk_columns: { + id: $id + }, + _set: { + ${query} + } + ) { + id + } + } + `, + variables: { + id: id, + }, + }; + + let response = await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + let result = response?.data?.data?.update_observations_by_pk; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Observation updated successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to update obsevation !', + data: {}, + }); + } + } + + async updateFields(body: any, resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + body.enum = body?.enum + ? JSON.stringify(body?.enum).replace(/"/g, '\\"') + : ''; + + let vquery = `query MyQuery { + fields_aggregate(where: {title: {_eq:"${body?.title}"}, id: {_neq:${id}}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = vresponse?.data?.fields_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate title encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + var data = { + query: ` + mutation UpdateFields($id:Int!) { + update_fields_by_pk( + pk_columns: { + id: $id + }, + _set: { + ${query} + } + ) { + id + } + } + `, + variables: { + id: id, + }, + }; + + let response = await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + let result = response?.data?.data?.update_fields_by_pk; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Field updated successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to update Field !', + data: {}, + }); + } + } + + async updateObservationField(body: any, resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let vquery = `query MyQuery { + observation_fields_aggregate(where: {observation_id: {_eq:${body?.observation_id}}, field_id: {_eq:${body?.field_id}}, context_id: {_eq:${body?.context_id}}, id: {_neq:${id}}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = + vresponse?.data?.observation_fields_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate observation field encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + var data = { + query: ` + mutation UpdateObservationFields($id:Int!) { + update_observation_fields_by_pk( + pk_columns: { + id: $id + }, + _set: { + ${query} + } + ) { + id + } + } + `, + variables: { + id: id, + }, + }; + + let response = await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + let result = response?.data?.data?.update_observation_fields_by_pk; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Observation-Field updated successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to update obsevation-field !', + data: {}, + }); + } + } + + async updateFieldResponses(body: any, resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + body.updated_by = user_id; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let vquery = `query MyQuery { + field_responses_aggregate(where: {context_id: {_eq:${body?.context_id}},observation_fields_id:{_eq:${body?.observation_fields_id}}, field_id: {_eq:${body?.field_id}}, observation_id: {_eq:${body?.observation_id}}, response_value: {_eq:"${body?.response_value}"}, id: {_neq:${id}}}) { + aggregate { + count + } + } + }`; + + const vresponse = await this.hasuraServiceFromServices.getData({ + query: vquery, + }); + const newQdata = + vresponse?.data?.field_responses_aggregate?.aggregate?.count; + + if (newQdata > 0) { + return resp.status(422).json({ + success: false, + message: 'Duplicate field responses encountered !', + data: {}, + }); + } + + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e] && body[e] != '') { + if (e === 'render') { + query += `${e}: ${body[e]}, `; + } else if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } + }); + + var data = { + query: ` + mutation UpdateFieldResponses($id:Int!) { + update_field_responses_by_pk( + pk_columns: { + id: $id + }, + _set: { + ${query} + } + ) { + id + } + } + `, + variables: { + id: id, + }, + }; + + let response = await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + let result = response?.data?.data?.update_field_responses_by_pk; + + if (result) { + return resp.status(200).json({ + success: true, + message: 'Field Responses updated successfully!', + data: result, + }); + } else { + return resp.status(500).json({ + success: false, + message: 'Unable to update field Responses !', + data: {}, + }); + } + } + + async getObservationList(body: any, resp: any, request: any) { + let response; + let newQdata; + let query; + let obj_filters; + let data; + let user_id = request?.mw_userid; + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + if (body?.filters) { + const traverseFilters = (filters) => { + Object.keys(filters).forEach((key) => { + if (typeof filters[key] === 'object') { + traverseFilters(filters[key]); + } else { + if (!key.startsWith('_')) { + filters[`_${key}`] = filters[key]; + delete filters[key]; + } + } + }); + }; + + traverseFilters(body?.filters); + + data = { + query: `query Searchobservations($filters:observations_bool_exp) { + observations(where:$filters) { + created_at + created_by + id + name + title + observation_fields{ + id + observation_id + field_id + context + context_id + fields_sequence + fields{ + id + data_type + description + title + enum + } + } + updated_at + updated_by + } + }`, + variables: { + filters: body.filters, + }, + }; + } else { + data = { + query: `query MyQuery { + observations{ + id + name + title + observation_fields{ + id + observation_id + field_id + context + context_id + fields_sequence + fields{ + id + data_type + description + title + enum + } + } + created_at + created_by + updated_by + } + } + + `, + }; + } + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + newQdata = response?.data?.data?.observations; + + if (newQdata.length > 0) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getObservationByType(body: any, resp: any, request: any, type: any) { + let response; + let newQdata; + let query; + let obj_filters; + let data; + let user_id = request?.mw_userid; + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + if (body?.filters) { + function traverseFilters(filters) { + if (typeof filters === 'object') { + Object.keys(filters).forEach((key) => { + if (Array.isArray(filters[key])) { + filters[key].forEach((item) => + traverseFilters(item), + ); + } else if (typeof filters[key] === 'object') { + traverseFilters(filters[key]); + } else { + if (!key.startsWith('_')) { + filters[`_${key}`] = filters[key]; + delete filters[key]; + } + } + }); + } + } + + traverseFilters(body?.filters); + + if (type == 'forms') { + data = { + query: `query Searchobservations($filters:observations_bool_exp) { + observations(where:$filters) { + created_at + created_by + id + name + title + observation_fields{ + id + observation_id + field_id + context + context_id + fields_sequence + fields{ + id + data_type + description + title + enum + } + } + updated_at + updated_by + } + }`, + variables: { + filters: body.filters, + }, + }; + } else if (type == 'submissons') { + data = { + query: `query SearchObservations($observations: observations_bool_exp, $observation_fields: observation_fields_bool_exp,$field_responses: field_responses_bool_exp) { + observations(where: $observations) { + created_at + created_by + id + name + title + observation_fields(where: $observation_fields) { + id + observation_id + field_id + context + context_id + fields_sequence + fields { + id + data_type + description + title + enum + } + field_responses(where: $field_responses) { + id + context + context_id + observation_fields_id + response_value + } + } + updated_at + updated_by + } + }`, + variables: { + observations: body.filters?.observations, + observation_fields: body.filters?.observation_fields, + field_responses: body.filters?.field_responses, + }, + }; + } + } + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + newQdata = response?.data?.data?.observations; + + if (newQdata?.length > 0) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getFieldsList(body: any, resp: any, request: any) { + let response; + let newQdata; + let query; + let user_id = request?.mw_userid; + let data; + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + if (body?.filters) { + let filters = new Object(body); + + Object.keys(body.filters).forEach((item) => { + Object.keys(body.filters[item]).forEach((e) => { + if (!e.startsWith('_')) { + filters[item][`_${e}`] = filters[item][e]; + delete filters[item][e]; + } + }); + }); + + data = { + query: `query Searchfields($filters:fields_bool_exp) { + fields(where:$filters) { + created_at + created_by + id + name + updated_at + updated_by + data_type + description + extra_all_info + title + enum + } + }`, + variables: { + filters: body.filters, + }, + }; + } else { + data = { + query: `query MyQuery { + fields { + created_at + created_by + id + name + updated_at + updated_by + data_type + description + extra_all_info + title + enum + } + } + + + `, + }; + } + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + newQdata = response?.data?.data?.fields; + + if (newQdata.length > 0) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getObservationFieldList(body: any, resp: any, request: any) { + let response; + let newQdata; + let query; + let obj_filters; + let data; + let user_id = request?.mw_userid; + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + if (body?.filters) { + let filters = new Object(body); + + Object.keys(body.filters).forEach((item) => { + Object.keys(body.filters[item]).forEach((e) => { + if (!e.startsWith('_')) { + filters[item][`_${e}`] = filters[item][e]; + delete filters[item][e]; + } + }); + }); + + data = { + query: `query Searchobservation_fields($filters:observation_fields_bool_exp) { + observation_fields(where:$filters) { + created_at + created_by + id + observation_id + observations { + id + name + } + fields { + id + name + description + data_type + extra_all_info + } + context + context_id + fields_sequence + field_id + updated_at + updated_by + } + }`, + variables: { + filters: body.filters, + }, + }; + } else { + data = { + query: `query MyQuery { + observation_fields { + created_at + created_by + id + observation_id + observations{ + id + name + } + fields{ + id + name + description + data_type + extra_all_info + } + context + context_id + fields_sequence + field_id + updated_at + updated_by + } + } + + + `, + }; + } + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + newQdata = response?.data?.data?.observation_fields; + + if (newQdata.length > 0) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getFieldResponsesList(body: any, resp: any, request: any) { + let response; + let newQdata; + let query; + let obj_filters; + let data; + let user_id = request?.mw_userid; + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + if (body?.filters) { + let filters = new Object(body); + + Object.keys(body.filters).forEach((item) => { + Object.keys(body.filters[item]).forEach((e) => { + if (!e.startsWith('_')) { + filters[item][`_${e}`] = filters[item][e]; + delete filters[item][e]; + } + }); + }); + + data = { + query: `query Searchfield_responses($filters:field_responses_bool_exp) { + field_responses(where:$filters) { + created_at + created_by + id + observation_id + observation_fields_id + observation_fields{ + observations{ + id + name + } + fields{ + id + data_type + description + title + + } + } + context + context_id + response_value + updated_at + updated_by + } + }`, + variables: { + filters: body.filters, + }, + }; + } else { + data = { + query: `query MyQuery { + field_responses{ + created_at + created_by + id + observation_id + observation_fields_id + observation_fields{ + observations{ + id + name + } + fields{ + id + data_type + description + title + + } + } + context + context_id + response_value + updated_at + updated_by + } + } + + `, + }; + } + + response = await this.hasuraServiceFromServices.queryWithVariable(data); + + newQdata = response?.data?.data?.field_responses; + + if (newQdata.length > 0) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getObservationById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `query MyQuery { + observations_by_pk(id:${id}) { + id + name + title + created_by + created_at + updated_at + updated_by + } + } + + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.observations_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getFieldById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `query MyQuery { + fields_by_pk(id:${id}) { + created_at + created_by + id + name + updated_at + updated_by + data_type + description + extra_all_info + } + } + + + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.fields_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getObservationFieldById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `query MyQuery { + observation_fields_by_pk(id:${id}) { + created_at + created_by + id + observation_id + observations { + id + name + } + fields { + id + name + description + data_type + extra_all_info + } + context + context_id + fields_sequence + field_id + updated_at + updated_by + } + } + + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.observation_fields_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async getFieldResponsesById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `query MyQuery { + field_responses_by_pk(id:${id}) { + created_at + created_by + id + observation_id + observation_fields_id + observation_fields { + observations { + id + name + } + fields { + id + data_type + description + title + } + } + context + context_id + response_value + updated_at + updated_by + } + } + + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.field_responses_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async deleteObservationById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `mutation MyMutation { + delete_observations_by_pk(id:${id}) { + id + created_by + name + updated_at + updated_by + created_at + } + } + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.delete_observations_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data deleted successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async deleteFieldById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `mutation MyMutation { + delete_fields_by_pk(id:${id}) { + created_at + created_by + id + name + updated_at + updated_by + data_type + description + extra_all_info + } + } + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.delete_fields_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data deleted successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async deleteObservationFieldById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `mutation MyMutation { + delete_observation_fields_by_pk(id:${id}) { + created_at + created_by + id + observation_id + context + context_id + field_id + updated_at + updated_by + } + } + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.delete_observation_fields_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data deleted successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async deleteFieldResponsesById(resp: any, request: any, id: any) { + let user_id = request?.mw_userid; + + if (!id) { + return resp.status(422).json({ + message: 'Please provide a valid get id', + data: null, + }); + } + + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + let query = `mutation MyMutation { + delete_fields_responses_by_pk(id:${id}) { + created_at + created_by + id + observation_id + context + context_id + response_value + updated_at + updated_by + } + } + + `; + + const response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + const newQdata = response?.data?.delete_fields_responses_by_pk; + + if (newQdata) { + return resp.status(200).json({ + success: true, + message: 'Data deleted successfully!', + data: newQdata, + }); + } else { + return resp.json({ + status: 400, + message: 'Data Not Found', + data: {}, + }); + } + } + + async createFieldResponsesMany(bodyArray: any, resp: any, request: any) { + let user_id = request?.mw_userid; + let response; + let data; + let result; + if (!user_id) { + return resp.status(422).json({ + message: 'Invalid User Entity', + data: null, + }); + } + + for (let body of bodyArray) { + const { + observation_id, + context, + context_id, + field_id, + observation_fields_id, + ...rest + } = body; // Extract fields for querying + body.created_by = user_id; + body.updated_by = user_id; + + data = { + query: ` query GetFieldResponse { + field_responses( + where: { observation_id: { _eq: ${observation_id} }, context: { _eq: ${context} }, context_id: { _eq: ${context_id} },field_id:{_eq:${field_id}},observation_fields_id:{_eq:${observation_fields_id}} } + ) { + id + } + } + `, + }; + + const existingData = + await this.hasuraServiceFromServices.queryWithVariable(data); + const action = + existingData?.data?.data?.field_responses?.length > 0 + ? 'update' + : 'insert'; + let query = ''; + Object.keys(body).forEach((e) => { + if (body[e]) { + if (Array.isArray(body[e])) { + query += `${e}: "${JSON.stringify(body[e])}", `; + } else { + query += `${e}: "${body[e]}", `; + } + } else { + query += `${e}: "", `; + } + }); + + if (action == 'update') { + const id = existingData?.data?.data?.field_responses?.[0]?.id; + + data = { + query: ` mutation UpdateObservations($id:Int!) { + update_field_responses_by_pk( + pk_columns: { + id: $id + }, + _set: { + ${query} + } + ) { + id + } + } + `, + variables: { + id: id, + }, + }; + + response = + await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + result = response?.data?.data?.update_field_responses_by_pk; + } else { + data = { + query: `mutation CreateFieldsResponses { + insert_field_responses_one(object: {${query}}) { + id + observation_id + context + context_id + response_value + } + } + `, + variables: {}, + }; + + response = + await this.hasuraServiceFromServices.queryWithVariable( + data, + ); + + result = response?.data?.data?.insert_field_responses_one; + } + + console.log('result-->>', result); + if (!result) { + return resp.status(500).json({ + success: false, + message: 'Unable to create observation Field!', + data: {}, + }); + } + } + + return resp.status(200).json({ + success: true, + message: 'Observation Field(s) created successfully!', + data: result, + }); + } + + async getObservationReport(body: any, resp: any, req: any) { + let sql; + let dataWithStatus; + let observationFieldsResultCount; + + //get observation data from filters + + let observation_body = body?.filters?.observations; + let observation_fields_body = body?.filters?.observation_fields; + + sql = `select * from observations where name = '${observation_body?.name}'`; + + const observationData = ( + await this.hasuraServiceFromServices.executeRawSql(sql) + ).result; + + let observationResult = + this.hasuraServiceFromServices.getFormattedData(observationData); + + let observation_id = observationResult?.[0]?.id; + + //get observation_fields_data; + + sql = `select * from observation_fields where observation_id='${observation_id}' and context = '${observation_fields_body?.context}' and context_id = '${observation_fields_body?.context_id}'`; + const observationFieldsData = ( + await this.hasuraServiceFromServices.executeRawSql(sql) + )?.result; + + if (observationFieldsData == undefined) { + return resp.status(422).json({ + message: 'Data Not Found', + data: [], + }); + } + + let observationFieldsResult = + this.hasuraServiceFromServices.getFormattedData( + observationFieldsData, + ); + + let observationFieldIds = ''; + + observationFieldsResult.forEach((item) => { + observationFieldIds += `'${item.id}',`; + }); + + observationFieldIds = observationFieldIds.slice(0, -1); // Remove the trailing comma + + //get count of observation_fields + sql = `SELECT COUNT(*) FROM observation_fields WHERE observation_id='${observation_id}' AND context = '${observation_fields_body?.context}' and context_id = '${observation_fields_body?.context_id}' + `; + + const observationFieldsDataCount = ( + await this.hasuraServiceFromServices.executeRawSql(sql) + )?.result; + + if (observationFieldsDataCount == undefined) { + return resp.status(422).json({ + message: 'Data Not Found', + data: [], + }); + } + + observationFieldsResultCount = + this.hasuraServiceFromServices.getFormattedData( + observationFieldsDataCount, + ); + + //get data for fields_response + + let fields_response_body = body?.filters?.field_responses; + let fields_response_context = fields_response_body?.context; + let field_responses_context_id = fields_response_body?.context_id; + + sql = `SELECT + COALESCE(COUNT(fr.observation_id), 0) AS count, + all_combinations.observation_id, + all_combinations.context, + all_combinations.context_id + FROM + ( + + SELECT DISTINCT + observation_id, + '${fields_response_context}' AS context, + unnest(ARRAY[${field_responses_context_id}]) AS context_id + FROM + field_responses + WHERE + observation_id = ${observation_id} + AND observation_fields_id IN (${observationFieldIds}) + ) AS all_combinations + LEFT JOIN + field_responses AS fr + ON + all_combinations.observation_id = fr.observation_id + AND all_combinations.context = fr.context + AND all_combinations.context_id = fr.context_id + GROUP BY + all_combinations.observation_id, + all_combinations.context, + all_combinations.context_id; + `; + + const fieldResponsesData = ( + await this.hasuraServiceFromServices.executeRawSql(sql) + )?.result; + + if (fieldResponsesData == undefined) { + return resp.status(422).json({ + message: 'Data Not Found', + data: [], + }); + } + let fieldResponsesResult = + this.hasuraServiceFromServices.getFormattedData(fieldResponsesData); + + if (fieldResponsesResult.length > 0) { + dataWithStatus = this.addStatus( + fieldResponsesResult, + observationFieldsResultCount?.[0]?.count, + ); + return resp.status(200).json({ + message: 'Data retrieved', + data: dataWithStatus, + }); + } else { + //no data fouud + let fieldResponsesData = [ + ['count', 'observation_id', 'context', 'context_id'], + ]; + field_responses_context_id.forEach((item) => { + fieldResponsesData.push([ + '0', + `${observation_id}`, + `${fields_response_context}`, + `${item}`, + ]); + }); + + let fieldResponsesResult = + this.hasuraServiceFromServices.getFormattedData( + fieldResponsesData, + ); + + if (fieldResponsesResult.length > 0) { + dataWithStatus = this.addStatus( + fieldResponsesResult, + observationFieldsResultCount?.[0]?.count, + ); + return resp.status(200).json({ + message: 'Data retrieved', + data: dataWithStatus, + }); + } + + return resp.status(200).json({ + message: 'Data retrieved', + data: dataWithStatus, + }); + } + } + + addStatus(data, observationFieldsResultCount) { + for (const item of data) { + const count = parseInt(item.count); + if (count == observationFieldsResultCount) { + item.status = 'completed'; + } else if (count == 0) { + item.status = 'not_started'; + } else { + item.status = 'incomplete'; + } + } + return data; + } +} diff --git a/src/src/organisation/dto/create-organisation.dto.ts b/src/src/organisation/dto/create-organisation.dto.ts new file mode 100644 index 000000000..92183745a --- /dev/null +++ b/src/src/organisation/dto/create-organisation.dto.ts @@ -0,0 +1 @@ +export class CreateOrganisationDto {} diff --git a/src/src/organisation/dto/update-organisation.dto.ts b/src/src/organisation/dto/update-organisation.dto.ts new file mode 100644 index 000000000..daaf56ca0 --- /dev/null +++ b/src/src/organisation/dto/update-organisation.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateOrganisationDto } from './create-organisation.dto'; + +export class UpdateOrganisationDto extends PartialType(CreateOrganisationDto) {} diff --git a/src/src/organisation/entities/organisation.entity.ts b/src/src/organisation/entities/organisation.entity.ts new file mode 100644 index 000000000..9dd7007a2 --- /dev/null +++ b/src/src/organisation/entities/organisation.entity.ts @@ -0,0 +1 @@ +export class Organisation {} diff --git a/src/src/organisation/organisation.controller.spec.ts b/src/src/organisation/organisation.controller.spec.ts new file mode 100644 index 000000000..bf7ca925f --- /dev/null +++ b/src/src/organisation/organisation.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { OrganisationController } from './organisation.controller'; +import { OrganisationService } from './organisation.service'; + +describe('OrganisationController', () => { + let controller: OrganisationController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [OrganisationController], + providers: [OrganisationService], + }).compile(); + + controller = module.get(OrganisationController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/src/organisation/organisation.controller.ts b/src/src/organisation/organisation.controller.ts new file mode 100644 index 000000000..d001ef699 --- /dev/null +++ b/src/src/organisation/organisation.controller.ts @@ -0,0 +1,76 @@ +import { + Controller, + Get, + Post, + Body, + Req, + Res, + Delete, + UseGuards, + Response, + UsePipes, + ValidationPipe, + Param, +} from '@nestjs/common'; +import { OrganisationService } from './organisation.service'; +import { AuthGuard } from '../modules/auth/auth.guard'; +@Controller('organisation') +export class OrganisationController { + constructor(private readonly organisationService: OrganisationService) {} + + @Post('/create') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + create(@Body() body: any, @Req() request: any, @Res() response: Response) { + return this.organisationService.create(body, request, response); + } + + @Post('/list') + @UseGuards(new AuthGuard()) + getOrganisation( + @Body() body: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.organisationService.getOrganisation(body, req, response); + } + + @Post('/:id') + @UseGuards(new AuthGuard()) + getOrganisationDetails( + @Req() req: any, + @Res() response: Response, + @Param('id') id: number, + ) { + return this.organisationService.getOrganisationDetails( + req, + response, + id, + ); + } + + @Get('/exist_list') + @UseGuards(new AuthGuard()) + getOrganisationExists( + @Body() body: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.organisationService.getOrganisationExists( + body, + req, + response, + ); + } + + @Post('/add/existing') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + addExisting( + @Body() body: any, + @Req() request: any, + @Res() response: Response, + ) { + return this.organisationService.addExisting(body, request, response); + } +} diff --git a/src/src/organisation/organisation.module.ts b/src/src/organisation/organisation.module.ts new file mode 100644 index 000000000..c35b235eb --- /dev/null +++ b/src/src/organisation/organisation.module.ts @@ -0,0 +1,22 @@ +import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; +import { OrganisationService } from './organisation.service'; +import { OrganisationController } from './organisation.controller'; +import { HasuraModule as HasuraModuleFromServices } from '../services/hasura/hasura.module'; +import { HasuraModule } from '../hasura/hasura.module'; +import { CohortMiddleware } from 'src/common/middlewares/cohort.middleware'; +import { Method } from 'src/common/method/method'; + +@Module({ + imports: [HasuraModuleFromServices, HasuraModule], + controllers: [OrganisationController], + providers: [OrganisationService, Method], + exports: [OrganisationService], +}) +export class OrganisationModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer + .apply(CohortMiddleware) + .exclude() + .forRoutes(OrganisationController); + } +} diff --git a/src/src/organisation/organisation.service.spec.ts b/src/src/organisation/organisation.service.spec.ts new file mode 100644 index 000000000..bf37c9a63 --- /dev/null +++ b/src/src/organisation/organisation.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { OrganisationService } from './organisation.service'; + +describe('OrganisationService', () => { + let service: OrganisationService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [OrganisationService], + }).compile(); + + service = module.get(OrganisationService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/src/organisation/organisation.service.ts b/src/src/organisation/organisation.service.ts new file mode 100644 index 000000000..35029b301 --- /dev/null +++ b/src/src/organisation/organisation.service.ts @@ -0,0 +1,448 @@ +import { Injectable } from '@nestjs/common'; +import { HasuraService } from '../hasura/hasura.service'; +import { HasuraService as HasuraServiceFromServices } from '../services/hasura/hasura.service'; + +@Injectable() +export class OrganisationService { + constructor( + private hasuraService: HasuraService, + private hasuraServiceFromServices: HasuraServiceFromServices, + ) {} + + async create(body: any, request: any, response: any) { + let checkemail = { + query: `query MyQuery { + organisations_aggregate(where: {email_id: {_eq: "${body?.email_id}"}}){ + aggregate{ + count + } + } + }`, + }; + const emailcount = await this.hasuraServiceFromServices.getData( + checkemail, + ); + const count = + emailcount?.data?.organisations_aggregate?.aggregate?.count; + + if (count > 0) { + return response.status(422).send({ + success: false, + key: 'email_id', + message: 'Email ID Already Exists', + data: {}, + }); + } + const { + learner_target, + // doc_per_cohort_id, + // doc_per_monthly_id, + // doc_quarterly_id, + learner_per_camp, + camp_target, + } = body; + const missingFields = [ + 'learner_target', + // 'doc_per_cohort_id', + // 'doc_per_monthly_id', + // 'doc_quarterly_id', + 'learner_per_camp', + 'camp_target', + ].filter((field) => !body[field] && body[field] != ''); + + if (missingFields.length > 0) { + return response.status(422).send({ + success: false, + key: missingFields?.[0], + message: `Required fields are missing in the payload. ${missingFields.join( + ',', + )}`, + data: {}, + }); + } + const organisationData = { + name: body?.name, + mobile: body?.mobile, + contact_person: body?.contact_person, + address: body?.address || null, + email_id: body?.email_id, + }; + + const tableName = 'organisations'; + const newOrganisation = await this.hasuraService.q( + tableName, + organisationData, + ['name', 'mobile', 'contact_person', 'address', 'email_id'], + ); + + if (!newOrganisation || !newOrganisation?.organisations.id) { + throw new Error('Failed to create organisation.'); + } + const organisation = newOrganisation?.organisations; + + const organisation_id = organisation?.id; + + // Calculate learner_target per camp and round up to nearest whole number + if (Math.ceil(learner_target / learner_per_camp) !== camp_target) { + return response.status(422).send({ + success: false, + message: 'Camp target is wrong', + data: {}, + }); + } + + // Step 2: Insert data into the 'program_organisation' table + const programOrganisationTableName = 'program_organisation'; + const program_org = await this.hasuraService.q( + programOrganisationTableName, + { + organisation_id, + program_id: request.mw_program_id, + academic_year_id: request.mw_academic_year_id, + status: 'active', + ...body, + }, + [ + 'organisation_id', + 'program_id', + 'academic_year_id', + 'status', + 'learner_target', + // 'doc_per_cohort_id', + // 'doc_per_monthly_id', + // 'doc_quarterly_id', + 'learner_per_camp', + 'camp_target', + ], + ); + + // Return success response + response.status(200).json({ + success: true, + message: 'Organisation created successfully.', + data: { + organisation, + program_org: program_org?.program_organisation, + }, + }); + } + + public async getOrganisation(body: any, req: any, resp: any) { + const academic_year_id = req.mw_academic_year_id; + const program_id = req.mw_program_id; + + try { + const page = isNaN(body.page) ? 1 : parseInt(body.page); + const limit = isNaN(body.limit) ? 10 : parseInt(body.limit); + let offset = page > 1 ? limit * (page - 1) : 0; + let order_by = ''; + if (body?.order_by) { + let { name, id } = body?.order_by; + let errorData = {}; + if (name && !['asc', 'desc'].includes(name)) { + errorData = { + message: `Invalid value for order_by name ${name}`, + }; + } else if (id && !['asc', 'desc'].includes(id)) { + errorData = { + message: `Invalid value for order_by id ${id}`, + }; + } + if (Object.keys(errorData).length > 0) { + return resp.status(422).send({ + success: false, + ...errorData, + }); + } else { + const order = JSON.stringify({ name, id }).replace( + /"/g, + '', + ); + order_by = `, order_by:${order}`; + } + } + + let searchQuery = ''; + if (body.search && !isNaN(body.search)) { + let id = parseInt(body.search); + searchQuery = `id: {_eq: ${id}}`; + } else if (body.search) { + if (body.search && body.search !== '') { + let first_name = body.search.split(' ')[0]; + let last_name = body.search.split(' ')[1] || ''; + + if (last_name?.length > 0) { + searchQuery = `_and:[{name: { _ilike: "%${first_name}%" }}, {name: { _ilike: "%${last_name}%" }}],`; + } else { + searchQuery = `_or:[{name: { _ilike: "%${first_name}%" }}, {name: { _ilike: "%${first_name}%" }}],`; + } + } + } + + let data = { + query: `query MyQuery($limit:Int, $offset:Int) { + organisations_aggregate(where: {${searchQuery} + program_organisations: { + program_id:{_eq:${program_id}}, + academic_year_id:{_eq:${academic_year_id}} + status:{_eq:"active"} + }}){ + aggregate{ + count + } + } + organisations(where: {${searchQuery} + program_organisations: { + program_id:{_eq:${program_id}}, + academic_year_id:{_eq:${academic_year_id}} + status:{_eq:"active"} + } + }limit: $limit, + offset: $offset ${order_by},) { + id + name + contact_person + mobile + email_id + program_organisations(where:{program_id: {_eq: ${program_id}}, academic_year_id: {_eq: ${academic_year_id}}, status: {_eq: "active"}}){ + learner_target + } + + } + } + `, + variables: { + limit: limit, + offset: offset, + }, + }; + const response = await this.hasuraServiceFromServices.getData(data); + + const organisations = response?.data?.organisations || []; + const count = + response?.data?.organisations_aggregate?.aggregate?.count; + const totalPages = Math.ceil(count / limit); + return resp.status(200).send({ + success: true, + message: 'Organisation list found successfully', + data: organisations, + totalCount: count, + limit, + currentPage: page, + totalPages: totalPages, + }); + } catch (error) { + // Log error and return a generic error response + console.error('Error fetching organizations:', error); + return resp.status(422).send({ + success: false, + message: 'An error occurred while fetching organizations', + data: {}, + }); + } + } + + public async getOrganisationDetails(req: any, resp: any, id: any) { + const academic_year_id = req?.mw_academic_year_id; + const program_id = req?.mw_program_id; + const org_id = id; + try { + let data = { + query: `query MyQuery { + organisations(where: {id:{_eq:${org_id}} + }) { + id + name + contact_person + mobile + email_id + address + program_organisations(where:{program_id: {_eq: ${program_id}}, academic_year_id: {_eq: ${academic_year_id}}, status: {_eq: "active"}}){ + program_id + academic_year_id + status + organisation_id + learner_target + doc_per_cohort_id + doc_per_monthly_id + doc_quarterly_id + learner_per_camp + camp_target + program{ + name + state{ + state_name + } + } + } + } + } + `, + }; + + const response = await this.hasuraServiceFromServices.getData(data); + + const organisations = response?.data?.organisations || []; + + if (organisations.length == 0) { + return resp.status(422).send({ + success: false, + message: 'Organisation Details Not found!', + data: organisations, + }); + } else { + return resp.status(200).send({ + success: true, + message: 'Organisation Details found successfully!', + data: organisations?.[0], + }); + } + } catch (error) { + // Log error and return a generic error response + console.error('Error fetching organizations:', error); + return resp.status(422).send({ + success: false, + message: 'An error occurred while fetching organizations', + data: {}, + }); + } + } + + public async getOrganisationExists(body: any, req: any, resp: any) { + const academic_year_id = req?.mw_academic_year_id; + const program_id = req?.mw_program_id; + + try { + let data = { + query: `query MyQuery { + organisations(where: {_not: {program_organisations: {academic_year_id: {_eq: ${academic_year_id}}, program_id: {_eq: ${program_id}}}}}) { + id + name + } + } + `, + }; + + const response = await this.hasuraServiceFromServices.getData(data); + + const organisations = response?.data?.organisations || []; + + if (organisations.length > 0) { + return resp.status(200).send({ + success: true, + message: 'Organisation exists', + data: organisations, + }); + } else { + return resp.status(422).send({ + success: true, + message: 'Organisation not exists', + data: organisations, + }); + } + } catch (error) { + // Log error and return a generic error response + console.error('Error fetching organizations:', error); + return resp.status(422).send({ + success: false, + message: 'An error occurred while fetching organizations', + data: {}, + }); + } + } + + async addExisting(body: any, request: any, response: any) { + const { + organisation_id, + learner_target, + // doc_per_cohort_id, + // doc_per_monthly_id, + // doc_quarterly_id, + learner_per_camp, + camp_target, + } = body; + let data = { + query: `query MyQuery { + program_organisation_aggregate(where: {academic_year_id: {_eq: ${request.mw_academic_year_id}}, program_id: {_eq: ${request.mw_program_id}}, organisation_id: {_eq: ${organisation_id}}}) + { + aggregate{ + count + } + } + }`, + }; + const existing = await this.hasuraServiceFromServices.getData(data); + + const program_organisation = + existing?.data?.program_organisation_aggregate?.aggregate?.count; + + const missingFields = [ + 'organisation_id', + 'learner_target', + // 'doc_per_cohort_id', + // 'doc_per_monthly_id', + // 'doc_quarterly_id', + 'learner_per_camp', + 'camp_target', + ].filter((field) => !body[field] && body[field] != ''); + + if (missingFields.length > 0) { + return response.status(422).send({ + success: false, + key: missingFields?.[0], + message: `Required fields are missing in the payload. ${missingFields.join( + ',', + )}`, + data: {}, + }); + } + // Calculate learner_target per camp and round up to nearest whole number + if (Math.ceil(learner_target / learner_per_camp) !== camp_target) { + return response.status(422).send({ + success: false, + message: 'Camp target is wrong', + data: {}, + }); + } + + if (program_organisation == 0) { + const programOrganisationTableName = 'program_organisation'; + const program_organisation = await this.hasuraService.q( + programOrganisationTableName, + { + program_id: request.mw_program_id, + academic_year_id: request.mw_academic_year_id, + status: 'active', + ...body, + }, + [ + 'organisation_id', + 'program_id', + 'academic_year_id', + 'status', + 'learner_target', + // 'doc_per_cohort_id', + // 'doc_per_monthly_id', + // 'doc_quarterly_id', + 'learner_per_camp', + 'camp_target', + ], + ); + + // Return success response + response.status(200).json({ + success: true, + message: 'Existing Organisation created successfully.', + data: program_organisation, + }); + } else { + response.status(422).json({ + success: false, + key: 'organisation_id', + message: + 'Organisation ALready Exists for the Program and Academic Year.', + data: {}, + }); + } + } +} diff --git a/src/src/services/aws-rekognition/aws-rekognition.service.ts b/src/src/services/aws-rekognition/aws-rekognition.service.ts index 7d8dbaa66..eb0fe653c 100644 --- a/src/src/services/aws-rekognition/aws-rekognition.service.ts +++ b/src/src/services/aws-rekognition/aws-rekognition.service.ts @@ -251,7 +251,7 @@ export class AwsRekognitionService { Image: { S3Object: { Bucket: this.bucketName, - Name: modifiedImageName, + Name: originalImageName, }, }, ExternalImageId: modifiedImageName, @@ -345,8 +345,14 @@ export class AwsRekognitionService { //console.dir(compareResponse, { depth: 99 }); return compareResponse.UserMatches; } catch (error) { - console.log('searchUsersByImage:', error, error.stack); - return []; + console.log('code error ', error?.name); + if (error?.name == 'ProvisionedThroughputExceededException') { + console.log('ProvisionedThroughputExceededException'); + return false; + } else { + console.log('searchUsersByImage:', error, error.stack); + return []; + } } } diff --git a/src/src/upload-file/upload-file.service.ts b/src/src/upload-file/upload-file.service.ts index 39a50bcd5..754134e2d 100644 --- a/src/src/upload-file/upload-file.service.ts +++ b/src/src/upload-file/upload-file.service.ts @@ -18,7 +18,9 @@ export class UploadFileService { document_type: string, document_sub_type: string, response: Response, + isCommonFunction?, ) { + console.log('file-->>', file); if (!file?.originalname) { return response.status(400).send({ success: false, @@ -116,7 +118,10 @@ export class UploadFileService { }; const res = await this.hasuraService.postData(query); - if (res) { + if (res && isCommonFunction) { + return { data: { key: key, fileUrl: fileUrl, data: res.data } }; + } else if (res) { + console.log('response file upload-->>', JSON.stringify(res)); return response.status(200).send({ success: true, status: 'Success', @@ -230,6 +235,8 @@ export class UploadFileService { } } + console.log('name---->>', documentData.name); + const fileUrl = await this.s3Service.getFileUrl(documentData.name); let result; if (fileUrl) { diff --git a/src/src/user/user.controller.ts b/src/src/user/user.controller.ts index dc8a2299b..91fe5953c 100755 --- a/src/src/user/user.controller.ts +++ b/src/src/user/user.controller.ts @@ -233,6 +233,98 @@ export class UserController { return this.userService.validateOnBoardingLink(body, request, response); } + //get IP list + @Post('/ip/list') + @UseGuards(new AuthGuard()) + getIpList( + @Body() request: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.userService.getIpList(request, req, response); + } + //get cohort list of ip + @Post('/cohort/ip_list') + @UseGuards(new AuthGuard()) + getCohortIpList( + @Body() request: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.userService.getCohortIpList(request, req, response); + } + + @Post('/ip_users/list') + @UseGuards(new AuthGuard()) + getIpUserList( + @Body() body: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.userService.getIpUserList(body, req, response); + } + + @Post('/ip/:id') + @UseGuards(new AuthGuard()) + getIpDetails( + @Req() request: any, + @Body() body: any, + @Param('id') id: number, + @Res() response: any, + ) { + return this.userService.getIpDetails(id, body, request, response); + } + + @Post('/ip_users/exist_list') + @UseGuards(new AuthGuard()) + getIpUserListExists( + @Req() request: any, + @Body() body: any, + @Res() response: any, + ) { + return this.userService.getIpUserListExists(body, request, response); + } + + @Post('/roles/list') + @UseGuards(new AuthGuard()) + getRoleList( + @Body() body: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.userService.getRoleList(body, req, response); + } + + @Get('/cohort/academic_list') + @UseGuards(new AuthGuard()) + getCohortAcademicList( + @Body() request: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.userService.getCohortAcademicList(request, req, response); + } + + @Get('/cohort/program_list') + @UseGuards(new AuthGuard()) + getCohortProgramList( + @Body() request: Record, + @Req() req: any, + @Res() response: Response, + ) { + return this.userService.getCohortProgramList(request, req, response); + } + + @Post('/add/existing_ip') + @UsePipes(ValidationPipe) + @UseGuards(new AuthGuard()) + addExistingIp( + @Body() body: any, + @Req() request: any, + @Res() response: Response, + ) { + return this.userService.addExistingIp(body, request, response); + } /**************************************************************************/ /******************************* V2 APIs **********************************/ /**************************************************************************/ diff --git a/src/src/user/user.module.ts b/src/src/user/user.module.ts index 02a172e7d..f789a968d 100644 --- a/src/src/user/user.module.ts +++ b/src/src/user/user.module.ts @@ -41,6 +41,10 @@ export class UserModule implements NestModule { '/users/cohorts/my/:type', '/users/onboarding/validate', 'v2/users/is_user_exist/:role', + 'users/ip/list', + 'users/cohort/ip_list', + 'users/cohort/academic_list', + 'users/cohort/program_list', ) .forRoutes(UserController); } diff --git a/src/src/user/user.service.ts b/src/src/user/user.service.ts index 2f75f5254..35e125a25 100644 --- a/src/src/user/user.service.ts +++ b/src/src/user/user.service.ts @@ -45,8 +45,6 @@ export class UserService { ); const oldStatus = user?.program_faciltators?.status; const statusArray = [ - 'shortlisted_for_orientation', - 'selected_for_training', 'pragati_mobilizer', 'selected_for_onboarding', 'selected_prerak', @@ -258,7 +256,6 @@ export class UserService { public async ipUserInfo(request: any, res?, role: any = '') { let userData = null; - if (request.mw_userid) { if (role === 'staff') { userData = await this.getIpRoleUserById(request.mw_userid); @@ -280,10 +277,13 @@ export class UserService { }; } - public async getIpRoleUserById(id: any) { + public async getIpRoleUserById( + id: any, + filter: any = { program_id: 1, academic_year_id: 1 }, + ) { const data = await this.hasuraServiceFromServices.getOne(id, 'users', [ 'id', - 'program_users{organisation_id}', + `program_users(where:{program_id:{_eq:${filter?.program_id}}, academic_year_id:{_eq:${filter?.academic_year_id}}}){organisation_id program_id academic_year_id}`, 'first_name', ]); return data?.users; @@ -1475,7 +1475,7 @@ export class UserService { let cohort_academic_year_id; if (cohort_type == 'academic_year') { - if (role.includes('staff')) { + if (role.includes('staff') || role.includes('program_owner')) { const user = await this.ipUserInfo(req); if (!user?.data?.program_users?.[0]?.organisation_id) { return res.status(404).send({ @@ -1512,7 +1512,7 @@ export class UserService { } if (cohort_type == 'program') { - if (role.includes('staff')) { + if (role.includes('staff') || role.includes('program_owner')) { const user = await this.ipUserInfo(req); if (!user?.data?.program_users?.[0]?.organisation_id) { return res.status(404).send({ @@ -1542,7 +1542,7 @@ export class UserService { )?.result; } if (cohort_type == 'program_academic_year_id') { - if (role.includes('staff')) { + if (role.includes('staff') || role.includes('program_owner')) { const user = await this.ipUserInfo(req); if (!user?.data?.program_users?.[0]?.organisation_id) { return res.status(404).send({ @@ -1690,4 +1690,417 @@ export class UserService { return hasura_response; } + + //Get IP lIST + public async getIpList(body: any, req: any, resp: any) { + try { + let data = { + query: `query MyQuery { + organisations(order_by: {id: asc}) + { + id + name + } + } + `, + }; + const response = await this.hasuraServiceFromServices.getData(data); + + const organisations = response?.data?.organisations || []; + + return resp.status(200).send({ + success: true, + message: 'Organisation list found successfully', + data: organisations, + }); + } catch (error) { + // Log error and return a generic error response + console.error('Error fetching organizations:', error); + return resp.status(500).send({ + success: false, + message: 'An error occurred while fetching organizations', + data: {}, + }); + } + } + + //Get Cohort wise ip list + public async getCohortIpList(body: any, req: any, resp: any) { + const organisationId = body?.organisation_id; + if (!organisationId) { + return resp.status(422).send({ + success: false, + message: 'organisation_id is required', + data: {}, + }); + } + try { + let data = { + query: `query MyQuery { + program_organisation(where: {organisation_id: {_eq: ${organisationId}}}, order_by: {id: asc}) { + academic_year_id + program_id + organisation_id + academic_year { + name + } + program{ + name + state_id + state{ + state_name + } + } + } + } + `, + }; + const response = await this.hasuraServiceFromServices.getData(data); + + const list = response?.data?.program_organisation || []; + + return resp.status(200).send({ + success: true, + message: 'Academic Year Id and Program Id found successfully', + data: list, + }); + } catch (error) { + console.error('Error fetching cohort IP list:', error); + return resp.status(500).send({ + success: false, + message: 'An error occurred while fetching cohort IP list', + data: {}, + }); + } + } + + //get Ip-user user_id from organisation + public async getIPuser(req: any, res: any) { + let org_id = req.headers['x-ip-org-id']; + let mw_program_id = req.headers['x-program-id']; + let mw_academic_year_id = req.headers['x-academic-year-id']; + let tableName = 'program_users'; + + const data = { + query: `query MyQuery { + ${tableName}(where: {program_id: {_eq: ${mw_program_id}},academic_year_id: {_eq: ${mw_academic_year_id}},organisation_id:{_eq:${org_id}}, program_organisation: {status: {_eq: "active"}}}){ + id + user_id + role_slug + } + }`, + }; + + const result = await this.hasuraServiceFromServices.getData(data); + return result; + } + + public async getIpUserList(body: any, req: any, resp: any) { + const academic_year_id = req.mw_academic_year_id; + const program_id = req.mw_program_id; + const organisation_id = body?.organisation_id; + if (!organisation_id) { + return resp.status(422).send({ + success: false, + message: 'Organisation ID required', + }); + } + const programUserFilter = `academic_year_id: { _eq: ${academic_year_id} }, + program_id: { _eq: ${program_id}},organisation_id:{_eq:${organisation_id}}`; + let onlyfilter = [ + 'id', + 'first_name', + 'last_name', + 'email_id', + 'mobile', + 'username', + ]; + body.filter = { + ...(body.filter || {}), + core: ` + program_users: { + ${programUserFilter} + }`, + }; + + const result = await this.hasuraServiceFromServices.getAll( + 'users', + [ + ...onlyfilter, + `program_users(where:{ + ${programUserFilter} + }){academic_year_id program_id status organisation_id role_id role_slug}`, + ], + { ...body, onlyfilter: [...onlyfilter, 'core'] }, + ); + + return resp.status(200).send({ + ...result, + success: true, + message: 'IP User List Found Successfully', + }); + } + + public async getIpDetails(id: any, body: any, req: any, resp) { + const ip_id = id; + const program_id = req.mw_program_id; + const academic_year_id = req.mw_academic_year_id; + + let qury = `query MyQuery { + users(where: {id: {_eq: ${ip_id}}, program_users: {academic_year_id: {_eq: ${academic_year_id}}, program_id: {_eq: ${program_id}}}}){ + id + first_name + middle_name + last_name + mobile + username + program_users(where:{academic_year_id:{_eq:${academic_year_id}},program_id:{_eq:${program_id}}}){ + user_id + academic_year_id + program_id + organisation_id + role_slug + programs{ + name + state{ + state_name + } + } + organisations{ + name + } + } + + } + } + `; + const data = { query: qury }; + const response = await this.hasuraServiceFromServices.getData(data); + const newQdata = response?.data?.users; + if (newQdata.length == 0) { + return resp.status(422).json({ + success: false, + message: 'Data Not found!', + data: {}, + }); + } else { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata || {}, + }); + } + } + + public async getIpUserListExists(body: any, req: any, resp) { + const program_id = req.mw_program_id; + const academic_year_id = req.mw_academic_year_id; + if (!body?.organisation_id || body?.organisation_id == '') { + return resp.status(422).json({ + success: false, + key: 'organisation_id', + message: 'organisation_id Not found!', + data: {}, + }); + } + + const query = `query MyQuery { + users(where: {program_users: {program_id: {_eq: ${program_id}},organisation_id:{_eq:${body?.organisation_id}}, academic_year_id: {_neq: ${academic_year_id}}}}) { + id + first_name + last_name + middle_name + program_users(where: {program_id: {_eq: ${program_id}},organisation_id:{_eq:${body?.organisation_id}}, academic_year_id: {_neq: ${academic_year_id}}}) { + academic_year_id + organisation_id + program_id + user_id + } + } + } + `; + const response = await this.hasuraServiceFromServices.getData({ + query, + }); + const newQdata = response?.data?.users; + if (newQdata.length == 0) { + return resp.status(422).json({ + success: false, + message: 'Data Not found!', + data: {}, + }); + } else { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata || {}, + }); + } + } + + public async getRoleList(body: any, req: any, resp) { + let qury = `query MyQuery { + roles(where: {_not: {slug: {_in: ["program_owner","facilitator","beneficiary","staff"]}}}) { + id + role_type + slug + actions + } + } + `; + const data = { query: qury }; + const response = await this.hasuraServiceFromServices.getData(data); + + const newQdata = response?.data?.roles; + + if (newQdata.length == 0) { + return resp.status(422).json({ + success: false, + message: 'Data Not found!', + data: {}, + }); + } else { + return resp.status(200).json({ + success: true, + message: 'Data found successfully!', + data: newQdata || {}, + }); + } + } + + public async getCohortAcademicList(body: any, req: any, resp: any) { + try { + let data = { + query: `query MyQuery { + academic_years{ + id + name + } + } + `, + }; + const response = await this.hasuraServiceFromServices.getData(data); + + const list = response?.data?.academic_years || []; + + return resp.status(200).send({ + success: true, + message: 'Academic Year Id found successfully', + data: list, + }); + } catch (error) { + console.error('Error fetching cohort IP list:', error); + return resp.status(500).send({ + success: false, + message: 'An error occurred while fetching cohort IP list', + data: {}, + }); + } + } + + public async getCohortProgramList(body: any, req: any, resp: any) { + try { + let data = { + query: `query MyQuery { + programs{ + id name + state{ + state_name + } + } + } + `, + }; + const response = await this.hasuraServiceFromServices.getData(data); + + const list = response?.data?.programs || []; + + return resp.status(200).send({ + success: true, + message: 'Program Id found successfully', + data: list, + }); + } catch (error) { + console.error('Error fetching cohort IP list:', error); + return resp.status(500).send({ + success: false, + message: 'An error occurred while fetching cohort IP list', + data: {}, + }); + } + } + + async addExistingIp(body: any, request: any, response: any) { + const user_id = body?.user_id; + const organisationId = body?.organisation_id; + const roleSlug = body?.role_slug; + // Check if required fields are present in the payload + if (!user_id) { + return response.status(422).send({ + success: false, + message: 'Required fields are missing in the payload.', + data: {}, + }); + } + let data = { + query: `query MyQuery { + program_users(where: {program_id: {_eq: ${request.mw_program_id}}, user_id: {_eq: ${user_id}}}) + { + academic_year_id + program_id + user_id + } + }`, + }; + + const existing = await this.hasuraServiceFromServices.getData(data); + + const pu_academic_year = + existing?.data?.program_users?.[0]?.academic_year_id; + + if (!pu_academic_year) { + response.status(422).json({ + success: false, + key: 'user_id', + message: 'IP Users Does Not Have state Access', + data: {}, + }); + } else if (pu_academic_year == request.mw_academic_year_id) { + response.status(422).json({ + success: false, + key: 'academic_year_id', + message: 'IP Users Already Exists in This Academic Year', + data: {}, + }); + } else { + // Check if required fields are present in the payload + if (!organisationId || !roleSlug || !user_id) { + return response.status(422).send({ + success: false, + message: 'Required fields are missing in the payload.', + data: {}, + }); + } + const programUsersData = { + organisation_id: body?.organisation_id, + program_id: request.mw_program_id, + academic_year_id: request.mw_academic_year_id, + user_id: user_id, + role_slug: body?.role_slug, + }; + + const programUserTableName = 'program_users'; + const program_users = await this.hasuraService.q( + programUserTableName, + programUsersData, + ); + + // // Return success response + response.status(200).json({ + success: true, + message: 'Existing IP User created successfully.', + data: program_users, + }); + } + } } diff --git a/src/src/userauth/userauth.controller.ts b/src/src/userauth/userauth.controller.ts index e3c88c0ce..4ff0e8ef7 100644 --- a/src/src/userauth/userauth.controller.ts +++ b/src/src/userauth/userauth.controller.ts @@ -3,18 +3,21 @@ import { Controller, Get, Param, - Patch, Post, Req, Res, UseGuards, - UseInterceptors, UsePipes, ValidationPipe, Response, + Request, + UploadedFile, + UseInterceptors, } from '@nestjs/common'; +import { AuthGuard } from 'src/modules/auth/auth.guard'; import { UserauthService } from './userauth.service'; +import { FileInterceptor } from '@nestjs/platform-express/multer'; @Controller('userauth') export class UserauthController { @@ -35,4 +38,50 @@ export class UserauthController { public async isUserExists(@Body() body: Body, @Res() response: Response) { return this.userauthService.isUserExists(body, response); } + + @Get('/facilitator/user-info') + @UsePipes(ValidationPipe) + public async getUserInfoDetails( + @Res() response: Response, + @Req() request: Request, + ) { + return this.userauthService.getUserInfoDetails(request, response); + } + + // @Post('/onboarding') + // @UsePipes(ValidationPipe) + // @UseGuards(new AuthGuard()) + // public async userOnboarding( + // @Body() body: Body, + // @Res() response: Response, + // @Req() request: Request, + // ) { + // return this.userauthService.userOnboarding(body, response, request); + // } + + @Post('/onboarding') + @UseGuards(new AuthGuard()) + @UsePipes(ValidationPipe) + @UseInterceptors(FileInterceptor('jsonpayload')) // 'jsonpayload' is the name of the field for the uploaded file + public async userOnboarding( + @UploadedFile() file: Express.Multer.File, + @Res() response: Response, + @Req() request: Request, + ) { + return this.userauthService.userOnboarding(file, response, request); + } + + @Get('/beneficiary/user-info/:id') + @UsePipes(ValidationPipe) + public async getUserInfoDetailsForBeneficiary( + @Res() response: Response, + @Req() request: Request, + @Param('id') id: number, + ) { + return this.userauthService.getUserInfoDetailsForBeneficiary( + request, + response, + id, + ); + } } diff --git a/src/src/userauth/userauth.module.ts b/src/src/userauth/userauth.module.ts index 87e9209be..5d40e3e63 100644 --- a/src/src/userauth/userauth.module.ts +++ b/src/src/userauth/userauth.module.ts @@ -1,4 +1,9 @@ -import { Module } from '@nestjs/common'; +import { + Module, + MiddlewareConsumer, + NestModule, + RequestMethod, +} from '@nestjs/common'; import { UserauthController } from './userauth.controller'; import { UserauthService } from './userauth.service'; import { HelperModule } from 'src/helper/helper.module'; @@ -9,6 +14,10 @@ import { UserModule } from 'src/user/user.module'; import { AuthService } from 'src/modules/auth/auth.service'; import { Method } from '../common/method/method'; import { AcknowledgementModule } from '../modules/acknowledgement/acknowledgement.module'; +import { CohortMiddleware } from 'src/common/middlewares/cohort.middleware'; +import { AuthMiddleware } from '../common/middlewares/auth.middleware'; +import { S3Module } from 'src/services/s3/s3.module'; +import { UploadFileService } from 'src/upload-file/upload-file.service'; @Module({ imports: [ @@ -18,9 +27,21 @@ import { AcknowledgementModule } from '../modules/acknowledgement/acknowledgemen HelperModule, UserModule, AcknowledgementModule, + S3Module, ], controllers: [UserauthController], - providers: [UserauthService, AuthService, Method], + providers: [UserauthService, UploadFileService, AuthService, Method], exports: [UserauthService], }) -export class UserauthModule {} +export class UserauthModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer + .apply(AuthMiddleware) + .exclude('/userauth/register/:role', 'userauth/is-user-exists') + .forRoutes(UserauthController); + consumer + .apply(CohortMiddleware) + .exclude('/userauth/register/:role', 'userauth/is-user-exists') + .forRoutes(UserauthController); + } +} diff --git a/src/src/userauth/userauth.service.ts b/src/src/userauth/userauth.service.ts index 3d2b12bbd..fc9ff300f 100644 --- a/src/src/userauth/userauth.service.ts +++ b/src/src/userauth/userauth.service.ts @@ -8,6 +8,16 @@ import { AuthService } from 'src/modules/auth/auth.service'; import { Method } from '../common/method/method'; import { AcknowledgementService } from 'src/modules/acknowledgement/acknowledgement.service'; import { UserService } from 'src/user/user.service'; +import { S3Service } from 'src/services/s3/s3.service'; +import { UploadFileService } from 'src/upload-file/upload-file.service'; +const { Blob } = require('buffer'); +const axios = require('axios'); +const fs = require('fs'); +const path = require('path'); + +//url to base64 +import fetch from 'node-fetch'; +import { Buffer } from 'buffer'; @Injectable() export class UserauthService { @@ -24,7 +34,9 @@ export class UserauthService { private authService: AuthService, private acknowledgementService: AcknowledgementService, private userService: UserService, + private readonly s3Service: S3Service, private method: Method, + private uploadFileService: UploadFileService, ) {} public async userAuthRegister(body, response, role) { @@ -138,9 +150,9 @@ export class UserauthService { if (findUsername.length > 0 && group === 'beneficiaries') { let lastUsername = findUsername[findUsername.length - 1].username; - console.log('lastUsername', lastUsername); + // //console.log('lastUsername', lastUsername); let count = findUsername.length; - console.log('count', count); + //console.log('count', count); data_to_create_user.username = data_to_create_user.username + '_' + count; } @@ -360,4 +372,1394 @@ export class UserauthService { }); } } + + public async fileUrlToBase64(imageUrl: string): Promise { + try { + const response = await fetch(imageUrl); + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + const base64Data = buffer.toString('base64'); + const fileType = response.headers.get('content-type'); // Get the content type from the response headers + const base64String = `data:${fileType};base64,${base64Data}`; // Include the content type in the Base64 string + return base64String; + } catch (error) { + //console.error('Error converting image to Base64:', error); + return null; + } + } + + public async getUserInfoDetails(request, response) { + let user_id = request.mw_userid; //get user id from token + let program_id = request?.mw_program_id; // get program_id from token + let academic_year_id = request?.mw_academic_year_id; // get academic_year_id from token + + //query to get user details information + ////console.log('user_id', user_id); + + let query = `query MyQuery { + users_by_pk(id:${user_id}) { + id + first_name + middle_name + last_name + dob + aadhar_no + mobile + alternative_mobile_number + email_id + state + district + block + grampanchayat + village + pincode + gender + username + mobile_no_verified + long + lat + keycloak_id + is_deactivated + is_duplicate + email_verified + duplicate_reason + aadhar_verified + aadhar_token + aadhaar_verification_mode + profile_photo_1 + profile_photo_1_documents: documents(where: {document_sub_type: {_eq: "profile_photo_1"}}) { + name + doument_type + document_sub_type + document_id: id + path + provider + context + context_id + } + profile_photo_2 + profile_photo_2_documents: documents(where: {document_sub_type: {_eq: "profile_photo_2"}}) { + name + doument_type + document_sub_type + document_id: id + path + provider + context + context_id + } + profile_photo_3 + profile_photo_3_documents: documents(where: {document_sub_type: {_eq: "profile_photo_3"}}) { + name + doument_type + document_sub_type + document_id: id + path + provider + context + context_id + } + core_faciltator { + device_type + device_ownership + has_diploma + diploma_details + pan_no + sourcing_channel + has_job_exp + has_volunteer_exp + } + extended_users { + marital_status + social_category + designation + qualification_id + } + references(where: {context: {_eq: "users"}}) { + name + designation + contact_number + context + } + program_faciltators(where: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}}}) { + id + parent_ip + documents_status + has_social_work_exp + police_verification_done + social_background_verified_by_neighbours + village_knowledge_test + status + form_step_number + availability + qualification_ids + } + experience(where: {type: {_in: ["experience","vo_experience"]}}) { + id + type + role_title + organization + description + experience_in_years + related_to_teaching + references(where: {context: {_eq: "experience"}}) { + id + name + contact_number + type_of_document + document_id + document_reference { + document_id: id + name + document_sub_type + doument_type + path + provider + context + context_id + } + } + } + qualifications{ + id + end_year + institution + start_year + qualification_master_id + qualification_reference_document_id + document_reference{ + document_id:id + name + path + provider + context + context_id + } + qualification_master{ + context + context_id + created_by + id + name + type + updated_by + } + } + } + } + `; + + const hasura_response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + let user_data = hasura_response?.data; + + ////console.log('user_data', JSON.stringify(user_data)); + + // get profile photo document details + let profilePhoto1Documents = + user_data?.users_by_pk?.profile_photo_1_documents; + + let profilePhoto2Documents = + user_data?.users_by_pk?.profile_photo_2_documents; + + let profilePhoto3Documents = + user_data?.users_by_pk?.profile_photo_3_documents; + + // modifiy individual profile photo document details as required + + // get file url and convert to base64 + let data_base64_profile_1 = null; + let profile_photo_1_info = {}; + if (profilePhoto1Documents?.[0]) { + const profile_photo_1_file_Url = await this.s3Service.getFileUrl( + profilePhoto1Documents?.[0]?.name, + ); + data_base64_profile_1 = await this.fileUrlToBase64( + profile_photo_1_file_Url, + ); + profile_photo_1_info = { + name: user_data?.users_by_pk?.profile_photo_1, + documents: { + base64: data_base64_profile_1, + document_id: profilePhoto1Documents?.[0]?.document_id, + name: profilePhoto1Documents?.[0]?.name, + document_type: profilePhoto1Documents?.[0]?.doument_type, + document_sub_type: + profilePhoto1Documents?.[0]?.document_sub_type, + path: profilePhoto1Documents?.[0]?.path, + provider: profilePhoto1Documents?.[0]?.provider, + context: profilePhoto1Documents?.[0]?.context, + context_id: profilePhoto1Documents?.[0]?.context_id, + }, + }; + } + let data_base64_profile_2 = null; + let profile_photo_2_info = {}; + if (profilePhoto2Documents?.[0]) { + const profile_photo_2_file_Url = await this.s3Service.getFileUrl( + profilePhoto2Documents?.[0]?.name, + ); + data_base64_profile_2 = await this.fileUrlToBase64( + profile_photo_2_file_Url, + ); + profile_photo_2_info = { + name: user_data?.users_by_pk?.profile_photo_2, + documents: { + base64: data_base64_profile_2, + document_id: profilePhoto2Documents?.[0]?.document_id, + name: profilePhoto2Documents?.[0]?.name, + document_type: profilePhoto2Documents?.[0]?.doument_type, + document_sub_type: + profilePhoto2Documents?.[0]?.document_sub_type, + path: profilePhoto2Documents?.[0]?.path, + provider: profilePhoto2Documents?.[0]?.provider, + context: profilePhoto2Documents?.[0]?.context, + context_id: profilePhoto2Documents?.[0]?.context_id, + }, + }; + } + let data_base64_profile_3 = null; + let profile_photo_3_info = {}; + if (profilePhoto3Documents?.[0]) { + const profile_photo_3_file_Url = await this.s3Service.getFileUrl( + profilePhoto3Documents?.[0]?.name, + ); + data_base64_profile_3 = await this.fileUrlToBase64( + profile_photo_3_file_Url, + ); + profile_photo_3_info = { + name: user_data?.users_by_pk?.profile_photo_3, + documents: { + base64: data_base64_profile_3, + document_id: profilePhoto3Documents?.[0]?.document_id, + name: profilePhoto3Documents?.[0]?.name, + document_type: + profilePhoto3Documents?.[0]?.doument_type || null, + document_sub_type: + profilePhoto3Documents?.[0]?.document_sub_type, + path: profilePhoto3Documents?.[0]?.path, + provider: profilePhoto3Documents?.[0]?.provider, + context: profilePhoto3Documents?.[0]?.context, + context_id: profilePhoto3Documents?.[0]?.context_id, + }, + }; + } + + if (!user_data?.users_by_pk) { + user_data.users_by_pk = {}; // Initialize as an empty object if it doesn't exist + } + // Replacing profile_photo_documents with profile_photo for all details + user_data.users_by_pk.profile_photo_1 = profile_photo_1_info; + user_data.users_by_pk.profile_photo_2 = profile_photo_2_info; + user_data.users_by_pk.profile_photo_3 = profile_photo_3_info; + + // Removing profile_photo_documents object + delete user_data.users_by_pk.profile_photo_1_documents; + delete user_data.users_by_pk.profile_photo_2_documents; + delete user_data.users_by_pk.profile_photo_3_documents; + + // update experience format + let experience_format = []; + if (user_data?.users_by_pk?.experience.length > 0) { + for ( + let i = 0; + i <= user_data?.users_by_pk?.experience.length; + i++ + ) { + let obj_experience = user_data?.users_by_pk?.experience[i]; + let obj_reference = obj_experience?.references[0]; + let temp_reference = {}; + if (obj_reference) { + let obj_document = obj_reference?.document_reference; + let temp_document = {}; + if (obj_document) { + let exp_document_base64 = null; + let obj_document_name = obj_document?.name; + if (obj_document_name) { + let obj_document_url = + await this.s3Service.getFileUrl( + obj_document_name, + ); + exp_document_base64 = await this.fileUrlToBase64( + obj_document_url, + ); + } + temp_document = { + base64: exp_document_base64, + document_id: obj_document?.document_id, + name: obj_document?.name, + document_sub_type: obj_document?.document_sub_type, + doument_type: obj_document?.doument_type, + path: obj_document?.path, + provider: obj_document?.provider, + context: obj_document?.context, + context_id: obj_document?.context_id, + }; + } + temp_reference = { + id: obj_reference?.id, + name: obj_reference?.name, + contact_number: obj_reference?.contact_number, + type_of_document: obj_reference?.type_of_document, + document_id: obj_reference?.document_id, + documents: temp_document, + }; + } + let temp_experience = { + id: obj_experience?.id, + type: obj_experience?.type, + role_title: obj_experience?.role_title, + organization: obj_experience?.organization, + description: obj_experience?.description, + experience_in_years: obj_experience?.experience_in_years, + related_to_teaching: obj_experience?.related_to_teaching, + references: temp_reference, + }; + experience_format.push(temp_experience); + } + } + user_data.users_by_pk.experience = experience_format; + + //update qualification format + let base64Qualifications = null; + let qualification_doc_name = + user_data.users_by_pk?.qualifications[0]?.document_reference?.name; + if (qualification_doc_name) { + let qualification_file_url = await this.s3Service.getFileUrl( + qualification_doc_name, + ); + base64Qualifications = await this.fileUrlToBase64( + qualification_file_url, + ); + } + user_data.users_by_pk.qualifications = + user_data?.users_by_pk?.qualifications?.reduce((acc, q) => { + const documents = q.document_reference + ? { + base64: base64Qualifications, + document_id: q?.document_reference?.document_id, + name: q?.document_reference?.name, + path: q?.document_reference?.path, + provider: q?.document_reference?.provider, + context: q?.document_reference?.context, + context_id: q?.document_reference?.context_id, + } + : {}; + + delete q.document_reference; // Remove document_reference + + // Update accumulator with updated qualification object + return { ...acc, ...q, documents }; + }, {}); + + user_data.users_by_pk.program_faciltators = + user_data?.users_by_pk?.program_faciltators?.reduce((acc, pf) => { + pf ? pf : {}; + + return { ...acc, ...pf }; + }, {}); + + user_data.users_by_pk.references = + user_data?.users_by_pk?.references?.reduce((acc, rf) => { + rf ? rf : {}; + + return { ...acc, ...rf }; + }, {}); + + const { + first_name, + middle_name, + last_name, + dob, + aadhar_no, + mobile, + alternative_mobile_number, + email_id, + state, + district, + block, + grampanchayat, + village, + pincode, + gender, + profile_photo_1, + profile_photo_2, + profile_photo_3, + username, + mobile_no_verified, + long, + lat, + keycloak_id, + is_deactivated, + is_duplicate, + email_verified, + duplicate_reason, + aadhar_verified, + aadhar_token, + aadhaar_verification_mode, + id, + } = user_data?.users_by_pk || {}; + + const formattedData = { + users: { + first_name, + middle_name, + last_name, + dob, + aadhar_no, + mobile, + alternative_mobile_number, + email_id, + state, + district, + block, + grampanchayat, + village, + pincode, + gender, + profile_photo_1, + profile_photo_2, + profile_photo_3, + username, + mobile_no_verified, + long, + lat, + keycloak_id, + is_deactivated, + is_duplicate, + email_verified, + duplicate_reason, + aadhar_verified, + aadhar_token, + aadhaar_verification_mode, + id, + }, + core_faciltators: user_data?.users_by_pk?.core_faciltator || {}, + extended_users: user_data?.users_by_pk?.extended_users || {}, + references: user_data?.users_by_pk?.references, + program_faciltators: user_data?.users_by_pk?.program_faciltators, + experience: user_data?.users_by_pk?.experience, + qualifications: user_data?.users_by_pk?.qualifications, + qualification_master: user_data?.users_by_pk?.qualification_master, + }; + + if (user_data) { + return response.status(200).json({ + message: 'Data retrieved successfully!', + data: formattedData, + }); + } + } + + public async userOnboarding(body: any, response: any, request: any) { + //first check validations for all inputs + let jsonContent; + //console.log('body-->', body); + + try { + // Convert buffer to JSON object + jsonContent = JSON.parse(body.buffer.toString('utf8')); + + // Process JSON content + + // Return success message + } catch (error) { + console.error('Error parsing JSON:', error); + } + + let user_id = request?.mw_userid; + let program_id = request?.mw_program_id; + let academic_year_id = request?.mw_academic_year_id; + + let result = await this.processTable( + jsonContent, + user_id, + program_id, + academic_year_id, + response, + ); + + //console.log('result-->>', result); + if (result) { + return response.status(200).json({ + result: result, + }); + } + } + + private async processTable( + json: any, + user_id: any, + program_id: any, + academic_year_id: any, + resp?: any, + ) { + let tableFields; + let tableName; + let set_update; + let update_id; + let profile_photo_fields_1; + let documents_fields_1; + let profile_photo_fields_2; + let documents_fields_2; + let profile_photo_fields_3; + let documents_fields_3; + let profile_photo_1_value; + let documents_values_1; + let profile_photo_2_value; + let documents_values_2; + let profile_photo_3_value; + let documents_values_3; + let profile_documents_array = []; + let qualification_document_data; + let resultArray = []; + let upsert_records_result; + let base64result; + for (const key in json) { + const value = json[key]; + if (!(Object.keys(value).length === 0)) { + //console.log('value-->>', value); + if (typeof value === 'object') { + tableName = key; + tableFields = Object.keys(value); + for (const subKey in value) { + const subValue = value[subKey]; + + if (typeof subValue === 'object') { + if (subKey.startsWith('profile_photo_')) { + const profilePhotoValue = + Object.values(subValue); + const documentsValues = Object.values( + subValue.documents, + ); + const base64 = documentsValues?.[0]; + const documentDetails = { + document_type: 'profile_photo', + document_sub_type: subKey, + }; + + if (base64) { + await this.base64ToBlob( + base64, + user_id, + resp, + documentDetails, + ); + } + + // Add profile photo with its name value for inserting in users table + value[subKey] = profilePhotoValue?.[0]; + } + } + } + } + + if (Array.isArray(value)) { + // Handle array + tableName = key; + let tempvalue = []; + + for (let i = 0; i < value.length; i++) { + let tempobj = value[i]; + delete tempobj.status; + delete tempobj.unique_key; + tempvalue.push(tempobj); + } + await this.processJsonArray( + tempvalue, + tableName, + user_id, + resultArray, + resp, + ); + } + + if (tableName != 'users' && tableName != 'references') { + value.user_id = user_id; + tableFields.push('user_id'); + } + + if (tableName === 'users') { + if (typeof value?.alternative_mobile_number === 'string') { + value.alternative_mobile_number = null; + } + } + + if (tableName == 'program_faciltators') { + //console.log('vlaues-->>', value); + + value.program_id = program_id; + value.academic_year_id = academic_year_id; + try { + value.qualification_ids = JSON.stringify( + JSON.parse(value?.qualification_ids), + ).replace(/"/g, '\\"'); + } catch (e) {} + + tableFields.push('program_id'); + tableFields.push('academic_year_id'); + //console.log('vlaues123-->>', value); + } + + if (tableName == 'references') { + value.context_id = user_id; + tableFields.push('context_id'); + value.context = 'users'; + tableFields.push('context'); + value.program_id = program_id; + value.academic_year_id = academic_year_id; + tableFields.push('program_id'); + tableFields.push('academic_year_id'); + } + + if (tableName == 'qualifications') { + //console.log('qualvalue-->', value); + if (value?.documents) { + let base64 = value?.documents?.base64; + + //console.log('base64-->>', base64); + let document_details = { + document_type: 'qualifications', + document_sub_type: 'qualifications', + context: 'qualifications', + }; + + if (base64) { + base64result = await this.base64ToBlob( + base64, + user_id, + resp, + document_details, + ); + } + } + + //console.log('base64result-->.', base64result); + + value['qualification_reference_document_id'] = + base64result?.document_id; + tableFields = tableFields?.filter( + (field) => field !== 'documents', + ); + delete value?.documents; + //console.log('qualvalue123-->', value); + } + + let response = await this.findExisitingReccord( + tableName, + value, + user_id, + ); + + set_update = response?.set_update; + update_id = response?.id; + + if (tableName != 'experience') { + upsert_records_result = await this.upsertRecords( + set_update, + tableName, + tableFields, + value, + user_id, + update_id, + ); + + if (upsert_records_result?.[tableName]?.extensions) { + resultArray.push({ + [tableName]: { + status: false, + message: + upsert_records_result?.[tableName]?.message, + }, + }); + } else { + resultArray.push({ + [tableName]: { + status: true, + message: 'successfully updated the value', + }, + }); + } + } + + //console.log('upsert_records_result-->>', upsert_records_result); + } + } + + return resultArray; + } + + public async processJsonArray( + values, + tableName, + user_id, + resultArray?, + resp?, + ) { + let set_update; + let update_id; + let referenceFields; + let referenceData; + let documentFields; + let documentData; + let result; + let base64result; + + for (const obj of values) { + let tableFields = Object.keys(obj); + tableFields.push('user_id'); + obj.user_id = user_id; + + set_update = obj?.id ? 1 : 0; + update_id = obj?.id; + + //console.log('set->.', set_update); + if (set_update == 1) { + tableFields.push('id'); + } + + if (tableName == 'experience') { + if ('references' in obj) { + // Process 'references' array differently + referenceFields = [ + 'name', + 'contact_number', + 'type_of_document', + 'context', + 'context_id', + ]; + referenceData = { + name: obj?.references.name, + contact_number: obj?.references.contact_number, + type_of_document: obj?.references.type_of_document, + context: 'experience', + context_id: obj?.id, + }; + + if (set_update == 1) { + referenceData.context_id = obj?.id; + referenceData['id'] = obj?.references?.id; + } + } + + if ('documents' in obj.references) { + let base64 = obj.references?.documents?.base64; + + //console.log('base64-->>', base64); + let document_details = { + document_type: 'reference', + document_sub_type: 'reference', + context: 'experience', + }; + + if (base64) { + base64result = await this.base64ToBlob( + base64, + user_id, + resp, + document_details, + ); + } + + referenceData['document_id'] = base64result?.document_id; + referenceFields.push('document_id'); + + tableFields = tableFields?.filter( + (field) => field !== 'documents', + ); + } + + // remove references object from the main object to process the experience object + tableFields = tableFields?.filter( + (field) => field !== 'references', + ); + delete obj?.references; + } + + //console.log('referenceData-->>', referenceData); + + result = await this.upsertRecords( + set_update, + tableName, + tableFields, + obj, + user_id, + update_id, + ); + + if (tableName == 'experience' && referenceData) { + let update_id = referenceData?.id; + set_update = update_id ? 1 : 0; + if (!obj?.id) { + referenceData.context_id = result?.experience?.id; + } + let result1 = await this.upsertRecords( + set_update, + 'references', + referenceFields, + referenceData, + user_id, + update_id, + ); + + //console.log('references result--->>', result1); + } + + if (result?.[tableName]?.extensions) { + resultArray.push({ + [tableName]: { + status: false, + message: result?.[tableName]?.message, + }, + }); + } else { + resultArray.push({ + [tableName]: { + status: true, + message: 'successfully updated the value', + }, + }); + } + } + } + + public async findExisitingReccord(tablename, value, user_id) { + let query; + let response; + + switch (tablename) { + case 'users': { + query = `query MyQuery { + users(where: {id: {_eq:${user_id}}}){ + id, + mobile + } + }`; + + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: response?.data?.users?.length > 0 ? 1 : 0, + id: response?.data?.users?.[0]?.id, + }; + } + case 'core_faciltators': { + query = `query MyQuery { + core_faciltators(where: {user_id: {_eq:${user_id}}}){ + id + } + } + `; + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: + response?.data?.core_faciltators?.length > 0 ? 1 : 0, + id: response?.data?.core_faciltators?.[0]?.id, + }; + } + case 'extended_users': { + query = `query MyQuery { + extended_users(where: {user_id: {_eq:${user_id}}}){ + id + } + } + + `; + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: + response?.data?.extended_users?.length > 0 ? 1 : 0, + id: response?.data?.extended_users?.[0]?.id, + }; + } + case 'program_faciltators': { + query = `query MyQuery { + program_faciltators(where: {user_id: {_eq:${user_id}},program_id:{_eq:${value?.program_id}},academic_year_id:{_eq:${value?.academic_year_id}}}){ + id + } + } + + `; + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: + response?.data?.program_faciltators?.length > 0 ? 1 : 0, + id: response?.data?.program_faciltators?.[0]?.id, + }; + } + case 'references': { + query = `query MyQuery { + references(where: {contact_number: {_eq:${value?.contact_number}},context_id:{_eq:${user_id}}}){ + id + } + } + + `; + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: response?.data?.references?.length > 0 ? 1 : 0, + id: response?.data?.references?.[0]?.id, + }; + } + case 'qualifications': { + query = `query MyQuery { + qualifications(where: {user_id: {_eq:${user_id}}}){ + id + } + } + `; + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: + response?.data?.qualifications?.length > 0 ? 1 : 0, + id: response?.data?.qualifications?.[0]?.id, + }; + } + case 'experience': { + query = `query MyQuery { + experience(where: {user_id: {_eq:${user_id}},type:{_eq:${value?.type}}}){ + id + } + } + `; + response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + return { + set_update: response?.data?.experience?.length > 0 ? 1 : 0, + id: response?.data?.experience?.[0]?.id, + }; + } + + default: + return undefined; + } + } + + public async upsertRecords( + set_update, + tableName, + tableFields, + value, + user_id, + id?, + ) { + let result; + //console.log('value-->>', value); + if (set_update == 1 && id) { + result = await this.hasuraService.q( + tableName, + { + ...value, + id: id, + }, + tableFields, + true, + [tableFields], + ); + } else { + result = await this.hasuraService.q( + tableName, + { + ...value, + }, + tableFields, + false, + [tableFields], + ); + } + + return result; + } + + public async upsertProfileDocuments(profileDocumentArray) { + for (const profileDocument of profileDocumentArray) { + let result = await this.hasuraService.q( + 'documents', + { + ...profileDocument, + id: profileDocument.document_id, + }, + ['name', 'document_sub_type', 'doument_type', 'id'], + true, + ['name', 'document_sub_type', 'doument_type', 'id'], + ); + + //console.log('resuklt-->>', result); + } + } + + public async getUserInfoDetailsForBeneficiary( + request, + response, + userid: any, + ) { + let user_id = userid; //get user id from token + let program_id = request?.mw_program_id; // get program_id from token + let academic_year_id = request?.mw_academic_year_id; // get academic_year_id from token + + //query to get user details information + + let query = `query MyQuery { + users_by_pk(id:${user_id}) { + first_name + middle_name + last_name + dob + aadhar_no + mobile + alternative_mobile_number + email_id + state + district + block + grampanchayat + village + pincode + gender + profile_photo_1 + profile_photo_2 + profile_photo_3 + username + mobile_no_verified + long + lat + keycloak_id + is_deactivated + is_duplicate + email_verified + duplicate_reason + aadhar_verified + aadhar_token + aadhaar_verification_mode + id + profile_photo_1 + profile_photo_1_documents: documents(where: {document_sub_type: {_eq: "profile_photo_1"}}) { + name + doument_type + document_sub_type + document_id: id + path + provider + context + context_id + } + profile_photo_2 + profile_photo_2_documents: documents(where: {document_sub_type: {_eq: "profile_photo_2"}}) { + name + doument_type + document_sub_type + document_id: id + path + provider + context + context_id + } + profile_photo_3 + profile_photo_3_documents: documents(where: {document_sub_type: {_eq: "profile_photo_3"}}) { + name + doument_type + document_sub_type + document_id: id + path + provider + context + context_id + } + core_beneficiaries { + career_aspiration + career_aspiration_details + device_ownership + device_type + type_of_learner + last_standard_of_education_year + last_standard_of_education + previous_school_type + reason_of_leaving_education + education_10th_exam_year + alternative_device_ownership + alternative_device_type + mother_first_name + mother_middle_name + mother_last_name + father_first_name + father_middle_name + father_last_name + } + extended_users { + marital_status + social_category + } + program_beneficiaries(where: {academic_year_id: {_eq:${academic_year_id}}, program_id: {_eq:${program_id}}}) { + learning_level + learning_motivation + type_of_support_needed + facilitator_id + status + } + references { + first_name + last_name + middle_name + relation + contact_number + context + context_id + } + } + } + + + `; + + const hasura_response = await this.hasuraServiceFromServices.getData({ + query: query, + }); + + let user_data = hasura_response?.data; + + // get profile photo document details + let profilePhoto1Documents = + user_data?.users_by_pk?.profile_photo_1_documents; + + let profilePhoto2Documents = + user_data?.users_by_pk?.profile_photo_2_documents; + + let profilePhoto3Documents = + user_data?.users_by_pk?.profile_photo_3_documents; + + // modifiy individual profile photo document details as required + + let profile_photo_1_info = { + name: user_data?.users_by_pk?.profile_photo_1, + documents: { + base64: null, + document_id: profilePhoto1Documents?.[0]?.document_id, + name: profilePhoto1Documents?.[0]?.name, + document_type: profilePhoto1Documents?.[0]?.doument_type, + document_sub_type: + profilePhoto1Documents?.[0]?.document_sub_type, + path: profilePhoto1Documents?.[0]?.path, + provider: profilePhoto1Documents?.[0]?.provider, + context: profilePhoto1Documents?.[0]?.context, + context_id: profilePhoto1Documents?.[0]?.context_id, + }, + }; + + let profile_photo_2_info = { + name: user_data?.users_by_pk?.profile_photo_2, + documents: { + base64: null, + document_id: profilePhoto2Documents?.[0]?.document_id, + name: profilePhoto2Documents?.[0]?.name, + document_type: profilePhoto2Documents?.[0]?.doument_type, + document_sub_type: + profilePhoto2Documents?.[0]?.document_sub_type, + path: profilePhoto2Documents?.[0]?.path, + provider: profilePhoto2Documents?.[0]?.provider, + context: profilePhoto2Documents?.[0]?.context, + context_id: profilePhoto2Documents?.[0]?.context_id, + }, + }; + + let profile_photo_3_info = { + name: user_data?.users_by_pk?.profile_photo_3, + documents: { + base64: null, + document_id: profilePhoto3Documents?.[0]?.document_id, + name: profilePhoto3Documents?.[0]?.name, + document_type: + profilePhoto3Documents?.[0]?.doument_type || null, + document_sub_type: + profilePhoto3Documents?.[0]?.document_sub_type, + path: profilePhoto3Documents?.[0]?.path, + provider: profilePhoto3Documents?.[0]?.provider, + context: profilePhoto3Documents?.[0]?.context, + context_id: profilePhoto3Documents?.[0]?.context_id, + }, + }; + + if (!user_data?.users_by_pk) { + user_data.users_by_pk = {}; // Initialize as an empty object if it doesn't exist + } + // Replacing profile_photo_documents with profile_photo for all details + user_data.users_by_pk.profile_photo_1 = profile_photo_1_info; + user_data.users_by_pk.profile_photo_2 = profile_photo_2_info; + user_data.users_by_pk.profile_photo_3 = profile_photo_3_info; + + // Removing profile_photo_documents object + delete user_data.users_by_pk.profile_photo_1_documents; + delete user_data.users_by_pk.profile_photo_2_documents; + delete user_data.users_by_pk.profile_photo_3_documents; + + user_data.users_by_pk.program_beneficiaries = + user_data?.users_by_pk?.program_beneficiaries?.reduce((acc, pb) => { + pb ? pb : {}; + + return { ...acc, ...pb }; + }, {}); + + user_data.users_by_pk.references = + user_data?.users_by_pk?.references?.reduce((acc, rf) => { + rf ? rf : {}; + + return { ...acc, ...rf }; + }, {}); + + const { + first_name, + middle_name, + last_name, + dob, + aadhar_no, + mobile, + alternative_mobile_number, + email_id, + state, + district, + block, + grampanchayat, + village, + pincode, + gender, + profile_photo_1, + profile_photo_2, + profile_photo_3, + username, + mobile_no_verified, + long, + lat, + keycloak_id, + is_deactivated, + is_duplicate, + email_verified, + duplicate_reason, + aadhar_verified, + aadhar_token, + aadhaar_verification_mode, + id, + } = user_data?.users_by_pk || {}; + + const formattedData = { + users: { + first_name, + middle_name, + last_name, + dob, + aadhar_no, + mobile, + alternative_mobile_number, + email_id, + state, + district, + block, + grampanchayat, + village, + pincode, + gender, + profile_photo_1, + profile_photo_2, + profile_photo_3, + username, + mobile_no_verified, + long, + lat, + keycloak_id, + is_deactivated, + is_duplicate, + email_verified, + duplicate_reason, + aadhar_verified, + aadhar_token, + aadhaar_verification_mode, + id, + }, + core_beneficiaries: user_data?.users_by_pk?.core_beneficiaries, + extended_users: user_data?.users_by_pk?.extended_users, + references: user_data?.users_by_pk?.references, + program_beneficiaries: + user_data?.users_by_pk?.program_beneficiaries, + }; + + if (user_data) { + return response.status(200).json({ + message: 'Data retrieved successfully!', + data: formattedData, + }); + } + } + + public async base64ToBlob(base64, userId, res, documentDetails) { + //console.log('here-->>'); + let fileObject; + const arr = base64.split(','); + const mime = arr[0].match(/:(.*?);/)[1]; + const buffer = Buffer.from(arr[1], 'base64'); + let { document_type, document_sub_type } = documentDetails; + + // Generate a unique filename with timestamp and userId + const now = new Date(); + const formattedDateTime = now + .toISOString() + .slice(0, 19) + .replace('T', '-'); // YYYY-MM-DD-HH-MM-SS format + const filename = `${userId}-${formattedDateTime}.${mime.split('/')[1]}`; // Extract file extension + + fileObject = { + fieldname: 'file', + mimetype: mime, + encoding: '7bit', + originalname: filename, + buffer: buffer, + }; + let uploadresponse = await this.uploadFileService.addFile( + fileObject, + userId, + document_type, + document_sub_type, + res, + true, + ); + + //console.log( + // 'response of file upload-->>', + // JSON.stringify(uploadresponse), + // ); + let document_id: any; // Adjust the type as per your requirement + + if ('data' in uploadresponse && uploadresponse.data) { + document_id = + uploadresponse.data.data?.insert_documents?.returning[0]?.id; + } else { + // Handle the case where 'data' property is not present + // or uploadresponse.data is null/undefined + document_id = null; // Or any other fallback value + } + return { + data: buffer, + filename, + mimeType: mime, + document_id: document_id, + }; + } }