diff --git a/background/background-process.html b/background/background-process.html index a125f893..822ec128 100644 --- a/background/background-process.html +++ b/background/background-process.html @@ -863,6 +863,17 @@

Background process

log.error(err) sendLogToUI(task,'error', "Error occured while clearing the baseline reference. Check logs for details."); } + + // + try{ + if(task === 'combine_csv_to_excel'){ + const result = await utils.combinedCSVsIntoExcel(options.csvDirectory); + sendLogToUI(task, result.status, result.message); + } + }catch(err){ + log.error(err) + sendLogToUI(task,'error', "Error occured while combining csv files. Check logs for details."); + } } diff --git a/background/background-utils.js b/background/background-utils.js index 152a4c11..3f2ec656 100644 --- a/background/background-utils.js +++ b/background/background-utils.js @@ -18,6 +18,7 @@ const bgUtils = window.require('./bg-utils'); const { VENDOR_CM_FORMATS, VENDOR_PM_FORMATS, VENDOR_FM_FORMATS, VENDOR_CM_PARSERS, VENDOR_PM_PARSERS, VENDOR_FM_PARSERS } = window.require('./vendor-formats'); const tems = window.require('./tems'); +const csvToExcelCombiner = window.require('./csv-to-excel-combiner'); //Fix PATH env variable on Mac OSX if(process.platform === 'darwin'){ @@ -1575,6 +1576,18 @@ async function clearBaselineReference(){ } } +async function combinedCSVsIntoExcel(csvDirectory){ + try{ + const combinedExcelFile = path.join(app.getPath('downloads'), 'combined_csv.xlsx'); + await csvToExcelCombiner.combine(csvDirectory, combinedExcelFile); + return {status: 'success', message: combinedExcelFile }; + }catch(e){ + log.error(e); + return {status: 'error', message: `Error occured while combining csv files. Check logs for details.`}; + } +} + +exports.combinedCSVsIntoExcel = combinedCSVsIntoExcel; exports.clearBaselineReference = clearBaselineReference; exports.importGISFile = importGISFile; exports.addParamToBaselineRef = addParamToBaselineRef; diff --git a/background/csv-to-excel-combiner.js b/background/csv-to-excel-combiner.js new file mode 100644 index 00000000..dd495d9d --- /dev/null +++ b/background/csv-to-excel-combiner.js @@ -0,0 +1,58 @@ +const csv = window.require('csvtojson'); +const Excel = window.require('exceljs'); + + +/* +* Combine csv files in a folder +* +* @param string csvFolder +*/ +async function combine(csvFolder, combineExcelFile){ + files = fs.readdirSync(csvFolder, { withFileTypes: true }).filter(dirent => !dirent.isDirectory()).map(dirent => dirent.name); + + var excelOptions = { + filename: combineExcelFile + }; + + //const workbook = new Excel.Workbook(); + var workbook = new Excel.stream.xlsx.WorkbookWriter(excelOptions); + + workbook.creator = 'Bodastage Solutions'; + + for(let i=0; i< files.length; i++) { + const fileName = files[i]; + const filePath = path.join(csvFolder, files[i]); + const sheetName = fileName.replace(".csv", ""); + + const worksheet = workbook.addWorksheet(sheetName); + + await new Promise((resolve, reject) => { + try{ + csv({noheader:true, output: "csv"}) + .fromFile(filePath) + .subscribe( (csvRow)=>{ + worksheet.addRow(csvRow).commit(); + + },(err) => {//onError + log.error(`CSVToExcelCombiner.csvJoJson.onError: ${err.toString()}`); + reject(); + }, + ()=>{//onComplete + resolve(undefined); + }); + }catch(e){ + log.error(e); + } + + }); + + await worksheet.commit(); + + }//eo-for + + await workbook.commit(); + //const res = await workbook.xlsx.writeFile(combineExcelFile); + +} + +exports.combine = combine; \ No newline at end of file diff --git a/package.json b/package.json index 1c810a71..e647252f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Boda-Lite", - "version": "0.3.0-beta.4", + "version": "0.3.0-rc.1", "description": "Boda-Lite is a telecommunication network management application", "private": true, "homepage": "./", diff --git a/src/index.js b/src/index.js index 5616e0cc..95523166 100644 --- a/src/index.js +++ b/src/index.js @@ -27,7 +27,8 @@ import { faLock, faAt, faSpinner, faHome, faPlug, faCog, faDownload, faFolder, faFile, faStar, faChevronRight, faDotCircle, faFolderOpen, faLink, faClock, faRss, faChartLine, faSquare, faTable, faInfoCircle ,faAsterisk, faFileAlt,faFrown,faDatabase, faFileExcel, faFileCsv, - faBroadcastTower, faPencilRuler, faBook,faCloudUploadAlt + faBroadcastTower, faPencilRuler, faBook,faCloudUploadAlt,faTools, + faCandyCane } from '@fortawesome/free-solid-svg-icons' library.add(faLock, faAt, faSpinner, faHome, faPlug, faCog, faDownload, @@ -37,7 +38,7 @@ faChartArea, faBrain, faGem, faUserMd, faGlobeAfrica, faPeopleCarry, faFolder, faFile, faStar, faChevronRight, faDotCircle, faFolderOpen, faLink, faClock, faRss, faChartLine, faSquare, faTable, faInfoCircle, faAsterisk, faFileAlt,faFrown, faDatabase, faFileExcel, faFileCsv, -faBroadcastTower, faPencilRuler, faBook, faCloudUploadAlt); +faBroadcastTower, faPencilRuler, faBook, faCloudUploadAlt, faTools, faCandyCane); const store = configureStore(); diff --git a/src/modules/dashboard/Dashboard.jsx b/src/modules/dashboard/Dashboard.jsx index ccc62913..34152684 100644 --- a/src/modules/dashboard/Dashboard.jsx +++ b/src/modules/dashboard/Dashboard.jsx @@ -140,6 +140,40 @@ class Dashboard extends React.Component { + + +
+ General + +
+ +
+
+ + +
+
Utilties
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
System diff --git a/src/modules/gis/gis-reducer.js b/src/modules/gis/gis-reducer.js index 9a38264d..5679eee1 100644 --- a/src/modules/gis/gis-reducer.js +++ b/src/modules/gis/gis-reducer.js @@ -25,9 +25,9 @@ const initialState = { //Radius of the sectors sectorRadius: { - 'gsm': 700, - 'umts':500, - 'lte': 250, + 'gsm': 300, + 'umts':200, + 'lte': 100, } }; diff --git a/src/modules/layout/SidePanel.jsx b/src/modules/layout/SidePanel.jsx index 776df088..d1913ece 100644 --- a/src/modules/layout/SidePanel.jsx +++ b/src/modules/layout/SidePanel.jsx @@ -4,11 +4,13 @@ import ReportsTree from '../reports/ReportsTree'; import GISLeftPanel from '../gis/GISLeftPanel'; import { connect } from 'react-redux'; import './sidepanel.css' +import UtilitiesSidePanel from '../utilities/UtilitiesSidePanel'; const SidePanels = { "DashboardSidePanel": DashboardSidePanel, "ReportsTree": ReportsTree, - "GISLeftPanel": GISLeftPanel + "GISLeftPanel": GISLeftPanel, + "UtilitiesSidePanel": UtilitiesSidePanel }; class SidePanel extends React.Component{ diff --git a/src/modules/layout/Tabs.jsx b/src/modules/layout/Tabs.jsx index 70947156..e3e6db33 100644 --- a/src/modules/layout/Tabs.jsx +++ b/src/modules/layout/Tabs.jsx @@ -16,6 +16,7 @@ import CreateCompositeReport from '../reports/CreateCompositeReport'; import GISMap from '../gis/GISMap'; import Baseline from '../baseline/Baseline'; import ParameterLibrary from '../telecomlib/ParameterLibrary'; +import CSVToExcelCombiner from '../utilities/CSVToExcelCombiner'; const Components = { "Help": Help, @@ -29,7 +30,8 @@ const Components = { "CreateCompositeReport": CreateCompositeReport, "GISMap": GISMap, "Baseline": Baseline, - "ParameterLibrary": ParameterLibrary + "ParameterLibrary": ParameterLibrary, + "CSVToExcelCombiner": CSVToExcelCombiner }; class Tabs extends React.Component { diff --git a/src/modules/utilities/CSVToExcelCombiner.jsx b/src/modules/utilities/CSVToExcelCombiner.jsx new file mode 100644 index 00000000..613eddbd --- /dev/null +++ b/src/modules/utilities/CSVToExcelCombiner.jsx @@ -0,0 +1,156 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { + FileInput, + Button , + Classes, + ProgressBar, + Intent, + +} from "@blueprintjs/core"; +import './utilities.css'; + +const { app, shell } = window.require('electron').remote; +const { ipcRenderer} = window.require("electron") +const fs = window.require('fs'); +const log = window.require('electron-log'); + +export default class CSVToExcelCombiner extends React.Component { + + static icon = "candy-cane"; + static label = "CSV to Excel Combiner" + + constructor(props){ + super(props); + + this.state = { + inputFolder: "Choose folder...", + processing: false, + notice: null + }; + + this.combinerListener = null; + } + + showFiles = () => { + if (!fs.existsSync(this.state.inputFolder)) { + this.setState({errorMessage: `${this.state.inputFolder} does not exist`}) + return; + } + shell.openItem(this.state.inputFolder) + } + + /** + * Update the input folder state when the text field value changes + */ + onInputFileChange = (e) => { + this.setState({inputFolder: e.target.files[0].path}) + } + + combineCSVFiles = () => { + //Confirm that the input folder exists + if( !fs.existsSync(this.state.inputFolder)){ + log.info(`Input folder: ${this.state.inputFolder} does not exist`); + this.setState( + { + notice: { + type: 'danger', + message: `Input folder: ${this.state.inputFolder} does not exist` + }, + processing: false + } + ); + return; + } + + let payload = { + csvDirectory: this.state.inputFolder + } + + //Set processing to true + this.setState({processing: true }); + + ipcRenderer.send('parse-cm-request', 'combine_csv_to_excel', JSON.stringify(payload)); + + this.combinerListener = (event, task, args) => { + const obj = JSON.parse(args) + if(task !== 'combine_csv_to_excel') return; + + //error + if(obj.status === 'error' && task === 'combine_csv_to_excel' ){ + this.setState({ + notice: {type: 'danger', message: obj.message}, + processing: false + }); + ipcRenderer.removeListener("parse-cm-request", this.combinerListener); + } + + //info + if(obj.status === 'info' && task === 'combine_csv_to_excel' ){ + this.setNotice('info', obj.message) + } + + if(obj.status === "success" && task === 'combine_csv_to_excel' ){ + this.setState({ + notice: { + type: 'success', + message: ` Combined file generated at: ${obj.message}` + }, + processing: false + }); + + shell.showItemInFolder(obj.message); + ipcRenderer.removeListener("parse-cm-request", this.combinerListener); + } + + } + ipcRenderer.on('parse-cm-request', this.combinerListener); + + + } + render(){ + let inputFolderEllipsis = this.state.inputFolder === 'Choose folder...' ? "" : "file-text-dir-rtl"; + + let notice = null; + if(this.state.notice !== null ){ + notice = (
{this.state.notice.message} + +
) + } + + return ( +
+ +
+ CSV to Excel Combiner + + { this.state.processing ? () : ""} + + {notice} + +
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ + +
+
+ ); + } +} \ No newline at end of file diff --git a/src/modules/utilities/UtilitiesSidePanel.jsx b/src/modules/utilities/UtilitiesSidePanel.jsx new file mode 100644 index 00000000..a5030774 --- /dev/null +++ b/src/modules/utilities/UtilitiesSidePanel.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import ReportsTree from '../reports/ReportsTree'; +import GISLeftPanel from '../gis/GISLeftPanel'; +import { connect } from 'react-redux'; +import { addTab, setSidePanel } from '../layout/uilayout-actions'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +class UtilitiesSidePanel extends React.Component { + constructor(props){ + super(props); + + this.addTab = this.addTab.bind(this); + + } + + + addTab = (options) => (e) => { + e.preventDefault(); + + let tabId = options.component; + this.props.dispatch(addTab(tabId, options.component, {title: options.title})); + } + + + render(){ + return ( +
+ Utilities + CSV to Excel Combiner + + + + + +
+ ); + + } +} + + +export default connect()(UtilitiesSidePanel); \ No newline at end of file diff --git a/src/modules/utilities/utilities.css b/src/modules/utilities/utilities.css new file mode 100644 index 00000000..3eefab11 --- /dev/null +++ b/src/modules/utilities/utilities.css @@ -0,0 +1,3 @@ +.file-text-dir-rtl { + direction: rtl !important; +} \ No newline at end of file diff --git a/src/version.js b/src/version.js index 58fce8bc..c15621e4 100644 --- a/src/version.js +++ b/src/version.js @@ -1,3 +1,3 @@ -export const VERSION = "0.3.0-beta.4"; +export const VERSION = "0.3.0-rc.1"; export default VERSION;