From 8889e9dfc035a8e9231b203700f88521e19fc844 Mon Sep 17 00:00:00 2001 From: Stig Ofstad Date: Tue, 9 Apr 2024 15:13:04 +0200 Subject: [PATCH] feat: create product UI --- api/src/calculators/fraction_interpolator.py | 4 - api/src/controllers/products.py | 8 + web/package.json | 4 +- web/src/Api.ts | 4 + web/src/Components/Navbar/CreateProduct.tsx | 146 +++++++++++++++++++ web/src/Components/Navbar/Navbar.tsx | 4 +- web/src/Types.ts | 6 + web/yarn.lock | 124 ++++++++-------- 8 files changed, 231 insertions(+), 69 deletions(-) create mode 100644 web/src/Components/Navbar/CreateProduct.tsx diff --git a/api/src/calculators/fraction_interpolator.py b/api/src/calculators/fraction_interpolator.py index eeeaa08..7456b48 100644 --- a/api/src/calculators/fraction_interpolator.py +++ b/api/src/calculators/fraction_interpolator.py @@ -7,8 +7,6 @@ def find_closest_bigger_index(array: list[float], target: float) -> int: index = bisect_left(array, target) - if index == 0: - raise ValueError("Failed to find closest biggest index") return index + 1 @@ -22,8 +20,6 @@ def fraction_interpolator_and_extrapolator( ) -> list[float]: sizes_dict = {size: 0 for size in zArray} # Populate size_dict with 0 values starting_index = find_closest_bigger_index(zArray, min(xArray)) - 1 - s = sum(yArray) - print(f"Sum Y: {s}") for zIndex, z in enumerate(zArray[starting_index:]): if z < xArray[0]: # Don't extrapolate down from first measuring point diff --git a/api/src/controllers/products.py b/api/src/controllers/products.py index b5ff6e4..b551ccc 100644 --- a/api/src/controllers/products.py +++ b/api/src/controllers/products.py @@ -45,6 +45,14 @@ def products_get(): def products_post(product_name: str, supplier_name: str, product_data: [[float, float]]) -> Response: product_id = sanitize_row_key(product_name) + + for p in product_data: + if not len(p) == 2: + return Response("Invalid product data. Must be two valid numbers for each line", 400) + + if not isinstance(p[0], float | int) or not isinstance(p[1], float | int): + return Response("Invalid product data. Must be two valid numbers for each line", 400) + sizes = [p[0] for p in product_data] cumulative = [p[1] for p in product_data] table_entry = { diff --git a/web/package.json b/web/package.json index f4f5133..11b3913 100644 --- a/web/package.json +++ b/web/package.json @@ -3,8 +3,8 @@ "version": "0.1.0", "private": true, "dependencies": { - "@equinor/eds-core-react": "^0.34.0", - "@equinor/eds-icons": "^0.19.3", + "@equinor/eds-core-react": "^0.36.1", + "@equinor/eds-icons": "^0.21.0", "@equinor/eds-tokens": "^0.9.2", "axios": "^1.6.2", "react": "^18.2.0", diff --git a/web/src/Api.ts b/web/src/Api.ts index 4944843..e432e75 100644 --- a/web/src/Api.ts +++ b/web/src/Api.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import { TNewProduct } from './Types' const BASE_PATH = '/api' @@ -39,6 +40,9 @@ class ProductsApi { async getProductsApi(token: string) { return axios.get(`${BASE_PATH}/products`, { headers: { Authorization: `Bearer ${token}` } }) } + async postProductsApi(token: string, newProduct: TNewProduct) { + return axios.post(`${BASE_PATH}/products`, newProduct, { headers: { Authorization: `Bearer ${token}` } }) + } } class FractionsApi { diff --git a/web/src/Components/Navbar/CreateProduct.tsx b/web/src/Components/Navbar/CreateProduct.tsx new file mode 100644 index 0000000..42dab0b --- /dev/null +++ b/web/src/Components/Navbar/CreateProduct.tsx @@ -0,0 +1,146 @@ +import React, { useContext, useState } from 'react' +// @ts-ignore +import { Button, Dialog, Icon, TextField, Table } from '@equinor/eds-core-react' + +import { ProductsAPI } from '../../Api' +import styled from 'styled-components' +import { ErrorToast } from '../Common/Toast' +import { AuthContext } from 'react-oauth2-code-pkce' +import { IAuthContext } from 'react-oauth2-code-pkce' +import { upload } from '@equinor/eds-icons' +import { TNewProduct } from '../../Types' + +const ButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + width: 100%; +` +const parseCumulativeProductCurve = (curve: string): [[number, number]] => { + // Split the input string into lines using the newline character + const lines = curve.split('\n') + + // Map each line into an array of two elements + const parsedData = lines.map(line => { + // Replace commas with periods to handle European-style decimals + const cleanLine = line.replace(/,/g, '.') + // Split each line by spaces or tabs to separate the numbers + const elements = cleanLine.split(/\s+|\t+/) + // Convert the string elements to numbers + return elements.map(element => parseFloat(element)) + }) + + return parsedData +} + +export const RefreshButton = () => { + const [dialogOpen, setDialogOpen] = useState(false) + const [loading, setLoading] = useState(false) + const { token }: IAuthContext = useContext(AuthContext) + const [newProduct, setNewProduct] = useState() + const [newProductData, setNewProductData] = useState([]) + + const postProduct = () => { + ProductsAPI.postProductsApi(token, { ...newProduct, productData: newProductData }) + .then(() => { + setLoading(false) + window.location.reload() + }) + .catch(error => { + ErrorToast(`${error.response.data}`, error.response.status) + console.error('fetch error' + error) + setLoading(false) + }) + } + + return ( + <> + + + + Define a new product + + +
+ setNewProduct({ ...newProduct, productName: event.target.value })} + /> + setNewProduct({ ...newProduct, productSupplier: event.target.value })} + /> +
+
+

+ Paste the product's measured data values here. Make sure it's been parsed correctly by inspecting the + table below before submitting. +

+

+ The format of the pasted data should be two numbers on each line (space or tab separated), where the first + number is the fraction size in micron of the measuring point, and the other the cumulative volume + percentage. +

+

+ The Optimizer requires each product to have 100 data points, from 0.01 - 3500 microns. If the data you + provide is missing data, the values will be interpolated and extrapolated. +

+ setNewProductData(parseCumulativeProductCurve(event.target.value))} + /> +
+
+ + + + Index + Fraction(micron) + Cumulative + + + {newProductData.map((dataPoint: any, index) => ( + + {index} + {dataPoint[0]} + {dataPoint[1]} + + ))} +
+
+
+ + + + + + +
+ + ) +} + +export default RefreshButton diff --git a/web/src/Components/Navbar/Navbar.tsx b/web/src/Components/Navbar/Navbar.tsx index 276861e..b1ea764 100644 --- a/web/src/Components/Navbar/Navbar.tsx +++ b/web/src/Components/Navbar/Navbar.tsx @@ -3,6 +3,7 @@ import RefreshButton from './RefreshButton' import { ContactButton } from './ContactButton' import { info_circle } from '@equinor/eds-icons' import { StyledLink } from './styles' +import CreateProduct from './CreateProduct' const Navbar = () => { return ( @@ -14,7 +15,7 @@ const Navbar = () => {
@@ -30,6 +31,7 @@ const Navbar = () => {
+
diff --git a/web/src/Types.ts b/web/src/Types.ts index 73ab73a..5dd3428 100644 --- a/web/src/Types.ts +++ b/web/src/Types.ts @@ -13,6 +13,12 @@ export interface Product { cumulative: Array | null } +export type TNewProduct = { + productName: string + productSupplier: string + productData: [[number, number]] +} + export interface Products { [id: string]: Product } diff --git a/web/yarn.lock b/web/yarn.lock index 967a445..8582c60 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1116,7 +1116,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.8", "@babel/runtime@^7.24.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== @@ -1290,35 +1290,35 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== -"@equinor/eds-core-react@^0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@equinor/eds-core-react/-/eds-core-react-0.34.0.tgz#ca965022851bf6d7565bb210d063a5368774b692" - integrity sha512-hkAuNhXk0/PylpEEtRobt3qeZyK7Hhu27pxMV6Y+StKi7ckuH7LJEisRAXxbUchFiv2tzNV7zxhdoPd5GjRp4w== +"@equinor/eds-core-react@^0.36.1": + version "0.36.1" + resolved "https://registry.yarnpkg.com/@equinor/eds-core-react/-/eds-core-react-0.36.1.tgz#b8f905f01976150bc4e6e56b427b25ba510ffa2e" + integrity sha512-cFpmsT4+EEFDhGE1DLNDT9Scr6SNBF4xnIfAgkMZcK6wmmZZT30lV2zdGgFC1JN9FfyvlisQukgpurynuBoJTw== dependencies: - "@babel/runtime" "^7.23.2" - "@equinor/eds-icons" "^0.19.3" + "@babel/runtime" "^7.24.0" + "@equinor/eds-icons" "^0.21.0" "@equinor/eds-tokens" "0.9.2" - "@equinor/eds-utils" "0.8.3" - "@floating-ui/react" "^0.26.2" - "@tanstack/react-virtual" "3.0.0-beta.54" - downshift "^8.2.3" + "@equinor/eds-utils" "0.8.4" + "@floating-ui/react" "^0.26.9" + "@tanstack/react-virtual" "3.1.3" + downshift "8.3.3" -"@equinor/eds-icons@^0.19.3": - version "0.19.3" - resolved "https://registry.yarnpkg.com/@equinor/eds-icons/-/eds-icons-0.19.3.tgz#08ce80c1ea6edf0b4144def73d1e5d0de392434f" - integrity sha512-Sh0W01PrwXPCi8/p9YKj0qKNtRU9R/xYJORinIzsNNRllpiu9wvuGAsQNE0gQaDDnrprsiRBH3+MdMSRXVs3Wg== +"@equinor/eds-icons@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@equinor/eds-icons/-/eds-icons-0.21.0.tgz#9adb994d2cd74011474a6d354458ffedcf7afc2f" + integrity sha512-k2keACHou9h9D5QLfSBeojTApqbPCkHNBWplUA/B9FQv8FMCMSBbjJAo2L/3yAExMylQN9LdwKo81T2tijRXoA== "@equinor/eds-tokens@0.9.2", "@equinor/eds-tokens@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@equinor/eds-tokens/-/eds-tokens-0.9.2.tgz#21545ffbb16a22f3eb7370295d32276dcb655373" integrity sha512-pDIFei0vsfN3GN12NKWqxskAkYBQd6+Dzjga2liuY81LfnlJs5g9NblamU9WY5w5YdVE5Z8FNjsMKDLs2JIWcw== -"@equinor/eds-utils@0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@equinor/eds-utils/-/eds-utils-0.8.3.tgz#c13db2f6a738a6d21206acd8ddb06a77c0cbf841" - integrity sha512-+Xm+BsXMUqlxZpPeGAJExhf27WqcQDHmKY9e4s6hfFP8D2vj5VcS0cglY86cmH4N3bDb4LmHL8V2FSX9xHr9+g== +"@equinor/eds-utils@0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@equinor/eds-utils/-/eds-utils-0.8.4.tgz#f2c33c4a04784aaff2a2b42f52b247312e8cd1dd" + integrity sha512-njvqXd3Hzfy5vkEqnx+uEBAu00vnG/5R+gDgWCReVDjjUoHdQNcrqfjBLsGF2UungtO0LbYV8YuBP+9l4V7ywQ== dependencies: - "@babel/runtime" "^7.23.2" + "@babel/runtime" "^7.23.8" "@equinor/eds-tokens" "0.9.2" "@eslint-community/eslint-utils@^4.2.0": @@ -1353,41 +1353,41 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6" integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA== -"@floating-ui/core@^1.4.2": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.1.tgz#62707d7ec585d0929f882321a1b1f4ea9c680da5" - integrity sha512-QgcKYwzcc8vvZ4n/5uklchy8KVdjJwcOeI+HnnTNclJjs2nYsy23DOCf+sSV1kBwD9yDAoVKCkv/gEPzgQU3Pw== +"@floating-ui/core@^1.0.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1" + integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g== dependencies: - "@floating-ui/utils" "^0.1.3" + "@floating-ui/utils" "^0.2.1" -"@floating-ui/dom@^1.5.1": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" - integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== +"@floating-ui/dom@^1.6.1": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef" + integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw== dependencies: - "@floating-ui/core" "^1.4.2" - "@floating-ui/utils" "^0.1.3" + "@floating-ui/core" "^1.0.0" + "@floating-ui/utils" "^0.2.0" -"@floating-ui/react-dom@^2.0.3": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.4.tgz#b076fafbdfeb881e1d86ae748b7ff95150e9f3ec" - integrity sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ== +"@floating-ui/react-dom@^2.0.0": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.8.tgz#afc24f9756d1b433e1fe0d047c24bd4d9cefaa5d" + integrity sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw== dependencies: - "@floating-ui/dom" "^1.5.1" + "@floating-ui/dom" "^1.6.1" -"@floating-ui/react@^0.26.2": - version "0.26.3" - resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.3.tgz#1ec435f35e37d5e34577ee89c7abb1eedb3a0c5d" - integrity sha512-iKH8WRR0L/nLiM6qavFZxkyegIZRMxGnM9aKEc71M4wRlUNkgTamjPsOQXy11oZbDOH37MiTbk/nAPn9M2+shA== +"@floating-ui/react@^0.26.9": + version "0.26.11" + resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.11.tgz#226d3fec890de439443b62f3138ef7de052b0998" + integrity sha512-fo01Cu+jzLDVG/AYAV2OtV6flhXvxP5rDaR1Fk8WWhtsFqwk478Dr2HGtB8s0HqQCsFWVbdHYpPjMiQiR/A9VA== dependencies: - "@floating-ui/react-dom" "^2.0.3" - "@floating-ui/utils" "^0.1.5" - tabbable "^6.0.1" + "@floating-ui/react-dom" "^2.0.0" + "@floating-ui/utils" "^0.2.0" + tabbable "^6.0.0" -"@floating-ui/utils@^0.1.3", "@floating-ui/utils@^0.1.5": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" - integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== +"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" + integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== "@humanwhocodes/config-array@^0.11.13": version "0.11.13" @@ -1942,17 +1942,17 @@ dependencies: tslib "^2.4.0" -"@tanstack/react-virtual@3.0.0-beta.54": - version "3.0.0-beta.54" - resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.0.0-beta.54.tgz#755979455adf13f2584937204a3f38703e446037" - integrity sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ== +"@tanstack/react-virtual@3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.1.3.tgz#4ef2a7dd819a7dd2b634d50cbd6ba498f06529ec" + integrity sha512-YCzcbF/Ws/uZ0q3Z6fagH+JVhx4JLvbSflgldMgLsuvB8aXjZLLb3HvrEVxY480F9wFlBiXlvQxOyXb5ENPrNA== dependencies: - "@tanstack/virtual-core" "3.0.0-beta.54" + "@tanstack/virtual-core" "3.1.3" -"@tanstack/virtual-core@3.0.0-beta.54": - version "3.0.0-beta.54" - resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.0.0-beta.54.tgz#12259d007911ad9fce1388385c54a9141f4ecdc4" - integrity sha512-jtkwqdP2rY2iCCDVAFuaNBH3fiEi29aTn2RhtIoky8DTTiCdc48plpHHreLwmv1PICJ4AJUUESaq3xa8fZH8+g== +"@tanstack/virtual-core@3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.1.3.tgz#77ced625f19ec9350f6e460f142b3be9bff03866" + integrity sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g== "@tootallnate/once@1": version "1.1.2" @@ -4199,10 +4199,10 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -downshift@^8.2.3: - version "8.2.3" - resolved "https://registry.yarnpkg.com/downshift/-/downshift-8.2.3.tgz#27106a5d9f408a6f6f9350ca465801d07e52db87" - integrity sha512-1HkvqaMTZpk24aqnXaRDnT+N5JCbpFpW+dCogB11+x+FCtfkFX0MbAO4vr/JdXi1VYQF174KjNUveBXqaXTPtg== +downshift@8.3.3: + version "8.3.3" + resolved "https://registry.yarnpkg.com/downshift/-/downshift-8.3.3.tgz#fef2804f09ffd013076f2dec6829559083c6c54f" + integrity sha512-f9znQFYF/3AWBkFiEc4H05Vdh41XFgJ80IatLBKIFoA3p86mAXc/iM9/XJ24loF9djtABD5NBEYL7b1b7xh2pw== dependencies: "@babel/runtime" "^7.22.15" compute-scroll-into-view "^3.0.3" @@ -8043,7 +8043,7 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-oauth2-code-pkce@^1.15.2: +react-oauth2-code-pkce@^1.17.2: version "1.17.2" resolved "https://registry.yarnpkg.com/react-oauth2-code-pkce/-/react-oauth2-code-pkce-1.17.2.tgz#a36fff30e33c131ed6bcdeeb7094536afad88448" integrity sha512-CIuvKlOr0r390NTvtEXSTCrNuGz7pu+Omf3LOcsEGuhvuLze3G4i7RjYVsqLjWALdvy40Wv3q5PV+qTbIryBoQ== @@ -9087,7 +9087,7 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -tabbable@^6.0.1: +tabbable@^6.0.0: version "6.2.0" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==