diff --git a/.gitignore b/.gitignore index 2494ccb8..0f852f2a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,9 @@ node_modules .idea /.vs/slnx.sqlite-journal .vs/slnx.sqlite +obj/Debug/netcoreapp5.0/amos-ss2021-carbon-footprint.csproj.AssemblyReference.cache frontend/yarn.lock +.vs/slnx.sqlite +.vs/amos-ss2021-carbon-footprint/v16/.suo +.vs/slnx.sqlite diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index 37102765..20d00776 100644 Binary files a/.vs/slnx.sqlite and b/.vs/slnx.sqlite differ diff --git a/frontend/package.json b/frontend/package.json index e31d4c6c..a03d2bf7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,7 @@ "@material-ui/core": "^4.11.4", "@react-pdf/renderer": "^2.0.12", "@reduxjs/toolkit": "^1.6.0", - "apexcharts": "^3.26.1", + "apexcharts": "^3.27.1", "axios": "^0.21.1", "cors": "^2.8.5", "html2canvas": "^1.0.0-rc.7", @@ -58,7 +58,7 @@ "@testing-library/react": "^11.2.7", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.6", - "gh-pages": "^2.2.0", + "gh-pages": "^3.2.3", "identity-obj-proxy": "^3.0.0", "jest": "26.6.0", "jest-transform-stub": "^2.0.0", diff --git a/frontend/src/components/details/ColumnChartComponent.jsx b/frontend/src/components/details/ColumnChartComponent.jsx index 71a30b76..3a9b6c18 100644 --- a/frontend/src/components/details/ColumnChartComponent.jsx +++ b/frontend/src/components/details/ColumnChartComponent.jsx @@ -1,18 +1,18 @@ -import React from 'react'; +import { React } from 'react'; import ReactApexChart from 'react-apexcharts'; import { getColumnChartData, getLifeCycleStages } from 'interface/projectInterface'; /** * Column Chart - * - * @author Julian Oelhaf + * Recieves the data from projectInterface.js using "getColumnChartData()" + * populates the data ito the column chart in Details Page. */ const ColumnChartComponent = () => { + const values = getColumnChartData(); const series = [ { name: 'Global warming in kg CO2 equivalents', - // TODO: this data needs to be recieved from backend - data: getColumnChartData() + data: values } ]; diff --git a/frontend/src/components/details/DetailsComponent.js b/frontend/src/components/details/DetailsComponent.js index 922e7f5e..b2f06451 100644 --- a/frontend/src/components/details/DetailsComponent.js +++ b/frontend/src/components/details/DetailsComponent.js @@ -6,13 +6,14 @@ import html2canvas from 'html2canvas'; import { Col, Container, Row } from 'react-grid-system'; import './navbar.css'; import { postCalculationRequest } from 'interface/BackendConnect'; -//import LoadingComponent from 'components/loading'; +import LoadingComponent from 'components/loading'; /** * the main component for detail page which includes * canvas page and variable drop down list * * @param props the recently selected model of a product. */ + class DetailsComponent extends Component { /* State consists of three variable one for each of the possible state * baselineScenario: only display the baseline scenario @@ -83,21 +84,29 @@ class DetailsComponent extends Component { pdf.save('invoice.pdf'); }); }; - + /* + * Important function that is given as the callback parameter to the postCalculationRequest in order to be called + * when the data processing is finished. Then the state stillLoading will be set to false. + * This change of state trigger the DetailsComponent to rerender and now display the charts and tables + * instead of the LoadingComponent. + */ + let handleFinishedDataRequest = () => { + this.setState({ stillLoading: false }); + }; const scenarioNames = { baseline: 'Baseline Scenario', modified: 'Modified Scenario' }; const { selectedProduct } = this.props; - postCalculationRequest(selectedProduct.productID); - - // if (this.state.stillLoading) { - // return ; - // } + if (this.state.stillLoading) { + postCalculationRequest(selectedProduct.productID, handleFinishedDataRequest); + return ; + } if (this.state.baselineScenario) { // if state equals baseline scenario only + console.log(selectedProduct); return ( diff --git a/frontend/src/components/details/MobileTableComponent.jsx b/frontend/src/components/details/MobileTableComponent.jsx index 2874322c..984f24e9 100644 --- a/frontend/src/components/details/MobileTableComponent.jsx +++ b/frontend/src/components/details/MobileTableComponent.jsx @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { Container } from 'react-grid-system'; import { getImpactCategoriesTableHeaders, - getImpactCategoriesTableData + getImpactAssessmentData } from 'interface/projectInterface'; import PropTypes from 'prop-types'; @@ -12,8 +12,8 @@ import PropTypes from 'prop-types'; */ class MobileTableComponent extends Component { state = { - headers: getImpactCategoriesTableHeaders(this.props.modelId), - rows: getImpactCategoriesTableData(this.props.modelId) + headers: getImpactCategoriesTableHeaders(), + rows: getImpactAssessmentData() }; render() { const idKey = this.props.tableKey; @@ -44,7 +44,8 @@ class MobileTableComponent extends Component { {header.value} - + {getRowDataHelper(idKey, index, this.state.rows)} + {/* {Object.values(this.state.rows[3])[index + 1]} @@ -52,38 +53,7 @@ class MobileTableComponent extends Component { {Object.values(this.state.rows[5])[index + 1]} - - - ))} - - - -
- - - {/* style needs to be defined in js */} - - {this.state.headers.map((header, index) => ( - - - - - + */} ))} @@ -94,10 +64,47 @@ class MobileTableComponent extends Component { } MobileTableComponent.propTypes = { - modelId: PropTypes.string.isRequired, + modelID: PropTypes.string.isRequired, productName: PropTypes.string.isRequired, modelName: PropTypes.string.isRequired, tableKey: PropTypes.string.isRequired }; export default MobileTableComponent; + +/** + * Makes life a little bit easier, and developers a little bit happier. + * (By translating the row data to column data for the Mobile component) + * + * @param {*} idKey - allows unique id to be generated + * @param {number} rowIndex - the index of the current row + * @param rows - the rowdata that will be accesed + * @returns the ; + case 1: + return ( + + ); + case 2: + return ; + + case 3: + return ; + case 4: + return ; + case 5: + return ; + case 6: + return ; + default: + return Material Composition - + @@ -63,7 +63,7 @@ class ScenarioComponent extends Component { > @@ -75,7 +75,7 @@ class ScenarioComponent extends Component { @@ -94,7 +94,7 @@ ScenarioComponent.propTypes = { onExportClick: PropTypes.func.isRequired, scenarioName: PropTypes.string, selectedProduct: PropTypes.shape({ - modelId: PropTypes.string, + modelID: PropTypes.string, modelName: PropTypes.string, productName: PropTypes.string }) diff --git a/frontend/src/components/details/SelectVariableComponent.jsx b/frontend/src/components/details/SelectVariableComponent.jsx index b2644354..4451af3e 100644 --- a/frontend/src/components/details/SelectVariableComponent.jsx +++ b/frontend/src/components/details/SelectVariableComponent.jsx @@ -3,7 +3,6 @@ import React, { Component } from 'react'; /** * a drop down component for selecting variable * - * @author Parham Gandomkar, Irem Toroslu */ class SelectVariableComponent extends Component { @@ -49,22 +48,18 @@ class SelectVariableComponent extends Component {
-
{this.state.variables.map((item) => ( - - ))}
diff --git a/frontend/src/components/details/TableComponent.jsx b/frontend/src/components/details/TableComponent.jsx index 3e42ca64..34f12b9f 100644 --- a/frontend/src/components/details/TableComponent.jsx +++ b/frontend/src/components/details/TableComponent.jsx @@ -5,54 +5,14 @@ import { getImpactCategoriesTableHeaders, getImpactAssessmentData } from 'interface/projectInterface'; -import LoadingComponent from 'components/loading'; /** * displays the impact catagories table of the selected model of the related product. */ class TableComponent extends Component { state = { - headers: [], - rows: [], - stillLoading: true + headers: getImpactCategoriesTableHeaders(), + rows: getImpactAssessmentData() }; - getData() { - let headerData = getImpactCategoriesTableHeaders(); - let rowsData = getImpactAssessmentData(); - - this.setState({ headers: headerData }); - - while ( - (rowsData && headerData === []) || - (rowsData && headerData === undefined) || - (rowsData && headerData === null) - ) { - rowsData = getImpactAssessmentData(); - headerData = getImpactCategoriesTableHeaders(); - return ; - } - console.log('getImpactAssessmentData#6'); - this.setState({ - rows: [ - { - key: 'row-1', - impactCategory: 'Global Warming', - unit: rowsData[5], - total: rowsData[4], - materialsLPT: rowsData[3], - manufacturing: rowsData[0], - operations: rowsData[2], - endOfLife: rowsData[1] - } - ] - }); - this.setState({ stillLoading: false }); - } - componentDidMount() { - this.getData(); - } - componentWillUnmount() { - console.log('unmount'); - } render() { const idKey = this.props.tableKey; return ( @@ -76,17 +36,21 @@ class TableComponent extends Component {
- {this.state.rows.map((item, index) => ( - - - - - - - - - - ))} + + + + + + + + +
- {header.value} - - {Object.values(this.state.rows[0])[index + 1]} - - {Object.values(this.state.rows[1])[index + 1]} - - {Object.values(this.state.rows[2])[index + 1]} -
element of the corresponding row + */ +function getRowDataHelper(idKey, rowIndex, rows) { + switch (rowIndex) { + case 0: + return {'Global Warming'} + {'kg CO'} + 2 + {' eq'}{' '} + {rows.get('Total')}{rows.get('Materials')}{rows.get('Manufacturing and Transportation')}{rows.get('Operation')}{rows.get('End of life')}; + } +} diff --git a/frontend/src/components/details/NavbarComponent.js b/frontend/src/components/details/NavbarComponent.js index a2664f91..b576c52e 100644 --- a/frontend/src/components/details/NavbarComponent.js +++ b/frontend/src/components/details/NavbarComponent.js @@ -8,7 +8,6 @@ import { useHistory } from 'react-router-dom'; * a divider Pannel for seperating search compoents and result components * and also providing the comparison feature by compare button * - * @author Parham Gandomkar, Irem Toroslu, Julian Oelhaf, Mani Anand */ const NavbarComponent = (props) => { const history = useHistory(); @@ -19,7 +18,7 @@ const NavbarComponent = (props) => {
history.goBack()}> -
{item.impactCategory}{item.unit}{item.total}{item.materialsLPT}{item.manufacturing}{item.operations}{item.endOfLife}
{'Global Warming'} + {'kg CO'} + 2 + {' eq'}{' '} + {this.state.rows.get('Total')}{this.state.rows.get('Materials')} + {this.state.rows.get('Manufacturing and Transportation')} + {this.state.rows.get('Operation')}{this.state.rows.get('End of life')}
@@ -97,7 +61,7 @@ class TableComponent extends Component { export default TableComponent; TableComponent.propTypes = { - modelId: PropTypes.string.isRequired, + modelID: PropTypes.string.isRequired, modelName: PropTypes.string.isRequired, productName: PropTypes.string.isRequired, tableKey: PropTypes.string.isRequired diff --git a/frontend/src/components/productGrid/LabelComponent.js b/frontend/src/components/productGrid/LabelComponent.js index 9835f794..01c1fa5c 100644 --- a/frontend/src/components/productGrid/LabelComponent.js +++ b/frontend/src/components/productGrid/LabelComponent.js @@ -4,7 +4,6 @@ import React from 'react'; /** * Displaying a label. * - * @author Mani Anand, Martin Wagner */ function LabelComponent({ productName }) { diff --git a/frontend/src/components/productGrid/ProductGridComponent.js b/frontend/src/components/productGrid/ProductGridComponent.js index d6e39bd7..c1446fbb 100644 --- a/frontend/src/components/productGrid/ProductGridComponent.js +++ b/frontend/src/components/productGrid/ProductGridComponent.js @@ -18,13 +18,12 @@ import { loadProjects } from 'store/actions/productAction'; * /** * * @returns the product and model properties - * @author Irem Toroslu, Martin Wagner, Mani Anand */ function ProductGridComponent({ selectedCategory }) { const [selectedProducts, setSelectedProducts] = useContext(PrivateSectionContext); const [productList] = useState([]); - const products = useSelector(state => state.products.data) + const products = useSelector((state) => state.products.data); const dispatch = useDispatch(); /* @@ -32,7 +31,7 @@ function ProductGridComponent({ selectedCategory }) { * hooks it so the Component reloads on change. At the moment the specified change is the selectedCategory. */ useEffect(() => { - dispatch(loadProjects(selectedCategory)) + dispatch(loadProjects(selectedCategory)); }, [selectedCategory, dispatch]); // TODO: We cannot keep the selection like this, if models are implemented. See #58 @@ -60,7 +59,7 @@ function ProductGridComponent({ selectedCategory }) { { + onClick={() => { // Save selection to ContextProvider newSelectedProducts[0].productID = product.productID; newSelectedProducts[0].productName = product.productName; diff --git a/frontend/src/interface/BackendConnect.js b/frontend/src/interface/BackendConnect.js index d1be655c..30a154e0 100644 --- a/frontend/src/interface/BackendConnect.js +++ b/frontend/src/interface/BackendConnect.js @@ -1,10 +1,5 @@ import axios from 'axios'; -import { - setMaterialCompositionLabels, - setMaterialCompositionData, - setImpactAssessmentData, - setColumnChartData -} from 'interface/projectInterface'; +import { processBackendData } from 'interface/processBackendData'; /** * Get request to det the details of all the projects from the API via backend. * @returns the list of all the projects. @@ -24,81 +19,32 @@ export async function getSimaProProjects() { const items = data; result = items.data.Result.Data; }); - console.log('API call to get the list of Products'); - console.log(result); + //console.log('API call to get the list of Products'); + //console.log(result); return result; } -/** - * Filters the materials data recieved from API. - * Filter the materials with Unit Value "Kg" and Values > 0. - * Maps the Material and its corresponding value. - * @param data data recieved from PostCalculationRequest - */ -async function materials(data) { - const items = data; - let materialData = items.data.Result.Results[0].Tables[0].Rows; - let finalMaterials = []; - let materialMap = new Map(); - for (let z = 0; z < materialData.length; z++) { - if (materialData[z][5] === 'kg') { - if (materialData[z][6] > 0) { - finalMaterials.push(materialData[z]); - } - } - } - for (let i = 0; i < finalMaterials.length; i++) { - materialMap.set(finalMaterials[i][0], finalMaterials[i][6]); - } - - setMaterialCompositionLabels(materialMap.keys()); - setMaterialCompositionData(materialMap.values()); -} -/** - * Filters the carbon impact data recieved from API. - * Filter the Carbon Values of GlobalWarming - * Maps the Carbon Values and its corresponding life cycle stage. - * @param data data recieved from PostCalculationRequest - */ -export async function carbonImpactData(data) { - console.log('Inside carbon impact data'); - const items = data; - let carbonData = items.data.Result.Results[0].Tables[1].Rows; - let impactMap = new Map(); - for (let i = 0; i < carbonData.length; i++) { - impactMap.set(carbonData[i][0], carbonData[i][2]); - } - console.log(impactMap); - setImpactAssessmentData(impactMap.values()); - setColumnChartData(); -} - /** * Post request to initiate the calculation for a project based on the project id and preset values. * This request is caught by the backend. * Which then checks if the calculation is stored based on the calculationId generated. * If the calculation is stored returns the results of calculation here. */ -export async function postCalculationRequest(projectId) { +export async function postCalculationRequest(projectId, callback) { // POST request using axios with set headers const headers = { Authorization: 'Bearer', 'Access-Control-Allow-Origin': 'POST', 'My-Custom-Header': 'foobar' }; - let result1; await axios .post(`https://localhost:44323/SimaPro/api/calculation/${projectId}`, { headers }) + .then(function (data) { - const items = data; - result1 = items.data.Result; - materials(data); - carbonImpactData(data); + processBackendData(data, callback); }); - console.log('Result'); - console.log(result1); } /** diff --git a/frontend/src/interface/processBackendData.js b/frontend/src/interface/processBackendData.js new file mode 100644 index 00000000..9e322ca9 --- /dev/null +++ b/frontend/src/interface/processBackendData.js @@ -0,0 +1,74 @@ +import { + setMaterialCompositionLabels, + setMaterialCompositionData, + setImpactAssessmentData, + setColumnChartData +} from 'interface/projectInterface'; + +/* + * Function to process the data recieved from the backend + * Filters the carbon impact data recieved from API. + * Filter the Carbon Values of GlobalWarming + * Maps the Carbon Values and its corresponding life cycle stage. + * Filters out the material composition labels and data + * Filters out all materials less than 1% + * Calculate the impact assesment in percent for the column charts + * + * @param data: data recieved from PostCalculationRequest + * @param callback: the handleFinishedDataRequest function from the DetailsComponent.js, this function is called at the end of the data process + * the function changes the state of the DetailsComponent thus triggering a rerender and causing the DetailsComponent to display the charts + * instead of the loading circle + */ +export function processBackendData(data, callback) { + const items = data; + /* + * Filter out the "correct" materials + * ignore everything that is not unit = 'kg", because there is also Energy and Power in the List with unit MJ + * ignore all the material that are small than 0kg, because scrap and waste materials are represended by a + * negativ value and we want to exclude srap and waste + */ + let materialData = items.data.Result.Results[0].Tables[0].Rows; + let finalMaterials = []; + let materialMap = new Map(); + var sumOfMaterials = 0; + for (let z = 0; z < materialData.length; z++) { + //check if unit is kg + if (materialData[z][5] === 'kg') { + //check if weight is positiv --> input material + if (materialData[z][6] > 0) { + sumOfMaterials = sumOfMaterials + Number(materialData[z][6]); + finalMaterials.push(materialData[z]); + } + } + } + console.log('Sum of Material:'); + console.log(sumOfMaterials); + for (let i = 0; i < finalMaterials.length; i++) { + if (Number(finalMaterials[i][6]) / sumOfMaterials > 0.01) { + materialMap.set(finalMaterials[i][0], finalMaterials[i][6]); + } + } + + let carbonData = items.data.Result.Results[0].Tables[1].Rows; + let impactMap = new Map(); + for (let i = 0; i < carbonData.length; i++) { + impactMap.set(carbonData[i][0], Number(carbonData[i][2]).toFixed(0)); + } + + let assessmentDataInPercent = []; + let assessmentValues = Array.from(impactMap.values()); + let total = assessmentValues[4]; + + for (let i = 0; i < assessmentValues.length - 2; i++) { + if (!isNaN(assessmentValues[i])) { + assessmentDataInPercent[i] = (Number(assessmentValues[i] / total) * 100).toFixed(1); + } + } + + setMaterialCompositionLabels(materialMap.keys()); + setMaterialCompositionData(materialMap.values()); + setImpactAssessmentData(impactMap); + setColumnChartData(assessmentDataInPercent); + + callback(); +} diff --git a/frontend/src/interface/projectInterface.js b/frontend/src/interface/projectInterface.js index 258ba071..704eb2d0 100644 --- a/frontend/src/interface/projectInterface.js +++ b/frontend/src/interface/projectInterface.js @@ -1,7 +1,6 @@ /** * The projectInterface is the interface between frontend and backend. * - * @author Martin Wagner, Julian Oelhaf */ import logo_1 from 'assets/dummyImages/Image_1.PNG'; @@ -9,10 +8,15 @@ import logo_3 from 'assets/dummyImages/Logo2.png'; var materialCompositionLabels; var materialCompositionData; -let materialDataInPercent = []; +let materialDataInPercent; var assessmentValues; let chartDataInPercent = []; +export function handOverBackendData(data) { + console.log('data'); + console.log(data); +} + /** * should get all the Products from the backend (soon) //TODO: declare and write. * @returns @@ -36,21 +40,17 @@ export function getModels() { /** * Gets the filtered Material Composititon Data from API - * inputs contribibuting less than 1% each, should have been filtered out by Siemens Energy + * inputs contribibuting less than 1% each, should have been filtered out processBackendData * Calculates the percentage values and returns them. * @param compositionData filtered data from backendconnnect */ export function setMaterialCompositionData(compositionData) { console.log('compositionData'); + materialDataInPercent = []; materialCompositionData = Array.from(compositionData); console.log(materialCompositionData); - let sum = 0; for (let i = 0; i < materialCompositionData.length; i++) { - sum += Number(materialCompositionData[i]); - } - console.log(sum); - for (let i = 0; i < materialCompositionData.length; i++) { - materialDataInPercent[i] = (Number(materialCompositionData[i]) / sum) * 100; + materialDataInPercent[i] = Number(materialCompositionData[i]); } console.log(materialDataInPercent); } @@ -58,7 +58,7 @@ export function setMaterialCompositionData(compositionData) { /** * Getter method to recieve the filtered Material Composititon Data from API */ -export async function getMaterialCompositionData() { +export function getMaterialCompositionData() { return materialDataInPercent; } @@ -68,38 +68,23 @@ export async function getMaterialCompositionData() { * @param compositionData filtered data from backendconnnect */ export function setMaterialCompositionLabels(compositionLabels) { - console.log('compositionLabels'); - materialCompositionLabels = Array.from(compositionLabels); - console.log(materialCompositionLabels); } /** * Getter method to recieve the filtered Material Composititon Labels from API */ -export async function getMaterialCompositionLabels() { +export function getMaterialCompositionLabels() { return materialCompositionLabels; - - // return [ - // 'Plywood', - // 'TotalSteel', - // 'GlueBeam', - // 'GlassFiber', - // 'Copper', - // 'Paper', - // 'Porcelain', - // 'Electronics', - // 'Aluminium' - // ]; } /** * Gets the Life Cycle Stages filtered from API * Impact Assessment is done for each of the life cycle stage - * @param modelId id of the model, which we want to get the Data + * @param modelID id of the model, which we want to get the Data */ -export function getLifeCycleStages(modelId) { +export function getLifeCycleStages(modelID) { return ['Materials', 'Manufacturing and Transport', 'Operation 30a (75% load)', 'End of Life']; } @@ -109,9 +94,7 @@ export function getLifeCycleStages(modelId) { * @param assessmentData recieved from Backendconnect */ export function setImpactAssessmentData(assessmentData) { - assessmentValues = Array.from(assessmentData); - console.log('Impact Assessment Data'); - console.log(assessmentValues); + assessmentValues = assessmentData; } /** @@ -127,22 +110,8 @@ export function getImpactAssessmentData() { * Percentage is calulated * @param assessmentData recieved from Backendconnect */ -export function setColumnChartData() { - console.log('Chart Assessment Data'); - console.log(assessmentValues); - let sum = 0; - for (let i = 0; i < assessmentValues.length; i++) { - if (!isNaN(assessmentValues[i])) { - sum += Number(assessmentValues[i]); - } - } - console.log(sum); - for (let i = 0; i < assessmentValues.length; i++) { - if (!isNaN(assessmentValues[i])) { - chartDataInPercent[i] = Number(assessmentValues[i] / sum) * 100; - } - } - console.log(chartDataInPercent); +export function setColumnChartData(assessmentDataInPercent) { + chartDataInPercent = assessmentDataInPercent; } /** * Getter method to recieve the filtered Impact Assessment Data from API @@ -152,9 +121,9 @@ export function getColumnChartData() { } /** * * QUESTION: life cycle stages fixed? - * @param modelId id of the model, for which we want to get the Data + * @param modelID id of the model, for which we want to get the Data */ -export function getImpactCategoriesTableHeaders(modelId) { +export function getImpactCategoriesTableHeaders(modelID) { return [ { key: 'header-1', value: 'Impact Category' }, { key: 'header-2', value: 'Unit' }, @@ -171,7 +140,5 @@ export function getImpactCategoriesTableHeaders(modelId) { * @param assessmentData recieved from Backendconnect and filtered here */ export function getImpactCategoriesTableData() { - console.log('Impact Categories Table Data'); - console.log(assessmentValues); return assessmentValues; }