From c277f6d32db9feece33e90f1d76e4a89ac34165b Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 20 Oct 2020 10:53:50 -0700 Subject: [PATCH] Added polling for watcher. --- electron/decoder/decoder.js | 10 ++-- electron/electron-store.js | 11 +++- electron/file-watcher/file-watcher.js | 20 ++++--- electron/ipc-main-handler.js | 17 ++++++ hasura/debug.log | 1 + .../1603204459690_run_sql_migration/down.yaml | 1 + .../1603204459690_run_sql_migration/up.yaml | 18 +++++++ .../1603204604474_run_sql_migration/down.yaml | 1 + .../1603204604474_run_sql_migration/up.yaml | 19 +++++++ .../atoms/data-label/data-label.atom.jsx | 26 +++++++++ .../part-type-converter.atom.jsx | 8 +-- .../watcher-polling.molecule.jsx | 54 +++++++++++++++++++ .../filepaths-list.organism.jsx | 1 - .../pages/settings/settings.page.jsx | 10 +++- src/ipc.types.js | 8 +++ src/ipc/ipc-renderer-handler.js | 5 ++ src/redux/application/application.actions.js | 4 ++ src/redux/application/application.reducer.js | 4 ++ .../application/application.selectors.js | 5 ++ src/redux/application/application.types.js | 1 + 20 files changed, 205 insertions(+), 19 deletions(-) create mode 100644 hasura/migrations/1603204459690_run_sql_migration/down.yaml create mode 100644 hasura/migrations/1603204459690_run_sql_migration/up.yaml create mode 100644 hasura/migrations/1603204604474_run_sql_migration/down.yaml create mode 100644 hasura/migrations/1603204604474_run_sql_migration/up.yaml create mode 100644 src/components/atoms/data-label/data-label.atom.jsx create mode 100644 src/components/molecules/watcher-polling/watcher-polling.molecule.jsx diff --git a/electron/decoder/decoder.js b/electron/decoder/decoder.js index 21c873b..0199932 100644 --- a/electron/decoder/decoder.js +++ b/electron/decoder/decoder.js @@ -330,11 +330,11 @@ async function DecodeLinFile(extensionlessFilePath) { (jobline) => jobline.part_type && !jobline.db_ref.startsWith("900") && - !jobline.db_ref.toLowerCase().startsWith("urethane") && - !jobline.db_ref.toLowerCase().startsWith("wheel") && - !jobline.db_ref.toLowerCase().startsWith("hazardous") && - !jobline.db_ref.toLowerCase().startsWith("detail") && - !jobline.db_ref.toLowerCase().startsWith("clean") && + !jobline.line_desc.toLowerCase().startsWith("urethane") && + !jobline.line_desc.toLowerCase().startsWith("wheel") && + !jobline.line_desc.toLowerCase().startsWith("hazardous") && + !jobline.line_desc.toLowerCase().startsWith("detail") && + !jobline.line_desc.toLowerCase().startsWith("clean") && jobline.part_type.toUpperCase() !== "PAG" && jobline.part_type.toUpperCase() !== "PAS" && jobline.part_type.toUpperCase() !== "PASL" && diff --git a/electron/electron-store.js b/electron/electron-store.js index 9d59c9c..44a6a03 100644 --- a/electron/electron-store.js +++ b/electron/electron-store.js @@ -1,5 +1,14 @@ const Store = require("electron-store"); -const store = new Store({ defaults: { filePaths: [], accepted_ins_co: [] } }); +const store = new Store({ + defaults: { + filePaths: [], + accepted_ins_co: [], + polling: { + enabled: false, + pollingInterval: 100, + }, + }, +}); exports.store = store; diff --git a/electron/file-watcher/file-watcher.js b/electron/file-watcher/file-watcher.js index edb58d6..7f31647 100644 --- a/electron/file-watcher/file-watcher.js +++ b/electron/file-watcher/file-watcher.js @@ -7,35 +7,38 @@ const { store } = require("../electron-store"); const { NewNotification, } = require("../notification-wrapper/notification-wrapper"); - +const log = require("electron-log"); var watcher; async function StartWatcher() { const filePaths = store.get("filePaths").map((fp) => path.join(fp, "**.[eE][nN][vV]")) || []; - console.log("StartWatcher -> filePaths", filePaths); - + log.info("StartWatcher -> filePaths", filePaths); + log.info("Use polling? ", store.get("polling").enabled); + if (filePaths.length === 0) { NewNotification({ title: "RPS Watcher cannot start", body: "Please set the appropriate file paths and try again.", }).show(); + log.warn("Cannot start watcher. No file paths set."); return []; } if (watcher) { try { - console.log("Trying to close watcher - it already existed."); + log.info("Trying to close watcher - it already existed."); await watcher.close(); - console.log("Watcher closed successfully!"); + log.info("Watcher closed successfully!"); } catch (error) { - console.log("Error trying to close Watcher.", error); + log.error("Error trying to close Watcher.", error); } } watcher = chokidar.watch(filePaths, { //ignored: /[\/\\]\./, + usePolling: store.get("polling").enabled, persistent: true, ignoreInitial: true, awaitWriteFinish: { @@ -88,7 +91,7 @@ function onWatcherReady() { async function StopWatcher() { await watcher.close(); - console.log("Watcher stopped."); + log.info("Watcher stopped."); const b = BrowserWindow.getAllWindows()[0]; b.webContents.send(ipcTypes.default.fileWatcher.toRenderer.stopSuccess); NewNotification({ @@ -111,12 +114,13 @@ async function HandleNewFile(path) { ipcTypes.default.estimate.toRenderer.estimateDecodeSuccess, newJob ); - + log.info(`Sent job for upload. ${newJob.clm_no}`); NewNotification({ title: "Job Uploaded", body: "A new job has been uploaded.", }).show(); } else { + log.info(`Ignored job. ${newJob.ERROR}`); NewNotification({ title: "Job Ignored", body: newJob.ERROR, diff --git a/electron/ipc-main-handler.js b/electron/ipc-main-handler.js index 6c69626..6a3da0d 100644 --- a/electron/ipc-main-handler.js +++ b/electron/ipc-main-handler.js @@ -17,3 +17,20 @@ ipcMain.on("test", async (event, object) => { ipcMain.on(ipcTypes.app.toMain.setAcceptableInsCoNm, (event, insCos) => { store.set("accepted_ins_co", insCos); }); + +ipcMain.on(ipcTypes.store.get, (event, key) => { + const val = store.get(key); + event.sender.send(ipcTypes.store.response, { [key]: val }); +}); + +ipcMain.on(ipcTypes.store.set, (event, key, val) => { + store.set(key, val); + + const st = store.get(); + event.sender.send(ipcTypes.store.response, st); +}); + +ipcMain.on(ipcTypes.store.getAll, (event, obj) => { + const val = store.get(); + event.sender.send(ipcTypes.store.response, val); +}); diff --git a/hasura/debug.log b/hasura/debug.log index ab51e52..3b38c55 100644 --- a/hasura/debug.log +++ b/hasura/debug.log @@ -1,2 +1,3 @@ [1014/195617.530:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) [1015/081931.328:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) +[1020/073641.000:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) diff --git a/hasura/migrations/1603204459690_run_sql_migration/down.yaml b/hasura/migrations/1603204459690_run_sql_migration/down.yaml new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/hasura/migrations/1603204459690_run_sql_migration/down.yaml @@ -0,0 +1 @@ +[] diff --git a/hasura/migrations/1603204459690_run_sql_migration/up.yaml b/hasura/migrations/1603204459690_run_sql_migration/up.yaml new file mode 100644 index 0000000..7f5cbcc --- /dev/null +++ b/hasura/migrations/1603204459690_run_sql_migration/up.yaml @@ -0,0 +1,18 @@ +- args: + cascade: true + read_only: false + sql: "CREATE OR REPLACE FUNCTION public.search_jobs(search text, startdate date, + enddate date)\n RETURNS SETOF jobs\n LANGUAGE plpgsql\n STABLE\nAS $function$ + BEGIN if search = '' then return query\nselect *\nfrom jobs j;\nelse\n\nif (startDate + is null) or (endDate is null) then \nreturn query\nSELECT *\nFROM jobs j2\nWHERE + \n\nownr_fn ILIKE '%' || search || '%'\n or ownr_ln ILIKE '%' || search || + '%'\n \n or clm_no ILIKE '%' || search || '%'\nORDER BY \n clm_no ILIKE + '%' || search || '%'\n OR null,\n ownr_fn ILIKE '%' || search || '%'\n + \ OR NULL,\n ownr_ln ILIKE '%' || search || '%'\n OR NULL;\nelse \nreturn + query\nSELECT *\nFROM jobs j2\nWHERE \nclose_date between startDate and endDate + and close_date is not null and\n(\nownr_fn ILIKE '%' || search || '%'\n or + ownr_ln ILIKE '%' || search || '%'\n \n or clm_no ILIKE '%' || search || + '%')\n\nORDER BY \n clm_no ILIKE '%' || search || '%'\n OR null,\n ownr_fn + ILIKE '%' || search || '%'\n OR NULL,\n ownr_ln ILIKE '%' || search || + '%'\n OR NULL;\n\nend if;\n\n\nend if;\nEND $function$;" + type: run_sql diff --git a/hasura/migrations/1603204604474_run_sql_migration/down.yaml b/hasura/migrations/1603204604474_run_sql_migration/down.yaml new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/hasura/migrations/1603204604474_run_sql_migration/down.yaml @@ -0,0 +1 @@ +[] diff --git a/hasura/migrations/1603204604474_run_sql_migration/up.yaml b/hasura/migrations/1603204604474_run_sql_migration/up.yaml new file mode 100644 index 0000000..6d0fe3b --- /dev/null +++ b/hasura/migrations/1603204604474_run_sql_migration/up.yaml @@ -0,0 +1,19 @@ +- args: + cascade: true + read_only: false + sql: "CREATE OR REPLACE FUNCTION public.search_jobs(search text, startdate date, + enddate date)\n RETURNS SETOF jobs\n LANGUAGE plpgsql\n STABLE\nAS $function$\nBEGIN + if search = '' and ((startDate is null) or (endDate is null)) then return query\nselect + *\nfrom jobs j;\nelse\n\nif (startDate is null) or (endDate is null) then \nreturn + query\nSELECT *\nFROM jobs j2\nWHERE \n\nownr_fn ILIKE '%' || search || '%'\n + \ or ownr_ln ILIKE '%' || search || '%'\n \n or clm_no ILIKE '%' || search + || '%'\nORDER BY \n clm_no ILIKE '%' || search || '%'\n OR null,\n ownr_fn + ILIKE '%' || search || '%'\n OR NULL,\n ownr_ln ILIKE '%' || search || + '%'\n OR NULL;\nelse \nreturn query\nSELECT *\nFROM jobs j2\nWHERE \nclose_date + between startDate and endDate and close_date is not null and\n(\nownr_fn ILIKE + '%' || search || '%'\n or ownr_ln ILIKE '%' || search || '%'\n \n or + clm_no ILIKE '%' || search || '%')\n\nORDER BY \n clm_no ILIKE '%' || search + || '%'\n OR null,\n ownr_fn ILIKE '%' || search || '%'\n OR NULL,\n + \ ownr_ln ILIKE '%' || search || '%'\n OR NULL;\n\nend if;\n\n\nend if;\nEND + $function$;" + type: run_sql diff --git a/src/components/atoms/data-label/data-label.atom.jsx b/src/components/atoms/data-label/data-label.atom.jsx new file mode 100644 index 0000000..e36060d --- /dev/null +++ b/src/components/atoms/data-label/data-label.atom.jsx @@ -0,0 +1,26 @@ +import React from "react"; + +export default function DataLabel({ + label, + hideIfNull, + children, + vertical, + visible = true, + ...props +}) { + if (!visible || (hideIfNull && !!!children)) return null; + + return ( +
+
{`${label}: `}
+
+ {children} +
+
+ ); +} diff --git a/src/components/atoms/part-type-converter/part-type-converter.atom.jsx b/src/components/atoms/part-type-converter/part-type-converter.atom.jsx index 624027a..c551e0a 100644 --- a/src/components/atoms/part-type-converter/part-type-converter.atom.jsx +++ b/src/components/atoms/part-type-converter/part-type-converter.atom.jsx @@ -1,14 +1,16 @@ export default (part_type) => { switch (part_type) { case "PAA": + case "PAL": + case "PAC": return "A/M"; case "PAE": return "Exist."; case "PAN": + case "PAP": return "OEM"; - case "PAL": - return "LKQ"; + default: - return "?"; + return part_type; } }; diff --git a/src/components/molecules/watcher-polling/watcher-polling.molecule.jsx b/src/components/molecules/watcher-polling/watcher-polling.molecule.jsx new file mode 100644 index 0000000..9a88bb9 --- /dev/null +++ b/src/components/molecules/watcher-polling/watcher-polling.molecule.jsx @@ -0,0 +1,54 @@ +import { InputNumber, Switch } from "antd"; +import React from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import ipcTypes from "../../../ipc.types"; +import { selectSettings } from "../../../redux/application/application.selectors"; +import DataLabel from "../../atoms/data-label/data-label.atom"; +const { ipcRenderer } = window; + +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + appSettings: selectSettings, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); + +export function WatcherPollingMolecule({ appSettings }) { + const handlePollingToggle = (val) => { + ipcRenderer.send(ipcTypes.default.store.set, { "polling.enabled": val }); + }; + const handleIntervalChange = (val) => { + ipcRenderer.send(ipcTypes.default.store.set, { + "polling.pollingInterval": val, + }); + }; + + return ( +
+ + + + + + +
+ ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(WatcherPollingMolecule); diff --git a/src/components/organisms/filepaths-list/filepaths-list.organism.jsx b/src/components/organisms/filepaths-list/filepaths-list.organism.jsx index 1e5c267..d883e12 100644 --- a/src/components/organisms/filepaths-list/filepaths-list.organism.jsx +++ b/src/components/organisms/filepaths-list/filepaths-list.organism.jsx @@ -21,7 +21,6 @@ export function FilePathsList({ watchedPaths }) { ipcRenderer.send(ipcTypes.default.fileWatcher.toMain.filepathsGet); }, []); - console.log("watchedPaths", watchedPaths); return (
Watcher File Paths diff --git a/src/components/pages/settings/settings.page.jsx b/src/components/pages/settings/settings.page.jsx index fbed9b4..f23e018 100644 --- a/src/components/pages/settings/settings.page.jsx +++ b/src/components/pages/settings/settings.page.jsx @@ -1,10 +1,17 @@ import { Col, Row } from "antd"; -import React from "react"; +import React, { useEffect } from "react"; +import ipcTypes from "../../../ipc.types"; +import WatcherPollingMolecule from "../../molecules/watcher-polling/watcher-polling.molecule"; import FilePathsListOrganism from "../../organisms/filepaths-list/filepaths-list.organism"; import ShopSettingsOrganism from "../../organisms/shop-settings/shop-settings.organism"; import WatcherManagerOrganism from "../../organisms/watcher-manager/watcher-manager.organism"; +const { ipcRenderer } = window; export default function SettingsPage() { + useEffect(() => { + ipcRenderer.send(ipcTypes.default.store.getAll); + }, []); + return (
@@ -13,6 +20,7 @@ export default function SettingsPage() { + diff --git a/src/ipc.types.js b/src/ipc.types.js index db36934..27ceace 100644 --- a/src/ipc.types.js +++ b/src/ipc.types.js @@ -9,6 +9,12 @@ exports.default = { setAcceptableInsCoNm: "setAcceptableInsCoNm", }, }, + store: { + get: "store__get", + getAll: "store_getAll", + set: "store_set", + response: "store_response", + }, fileWatcher: { toMain: { filepathsGet: "filewatcher__filepathsget", @@ -16,6 +22,7 @@ exports.default = { stop: "filewatcher__stop", addPath: "filewatcher__addPath", removePath: "filewatcher__removePath", + setPolling: "filewatcher__setPolling", }, toRenderer: { filepathsList: "filewatcher__filepathslist", @@ -23,6 +30,7 @@ exports.default = { startFailure: "filewatcher__start-failure", stopSuccess: "filewatcher__stop-success", error: "filewatcher__error", + getPolling: "filewatcher__getPolling", }, }, estimate: { diff --git a/src/ipc/ipc-renderer-handler.js b/src/ipc/ipc-renderer-handler.js index 75c956f..6fecb22 100644 --- a/src/ipc/ipc-renderer-handler.js +++ b/src/ipc/ipc-renderer-handler.js @@ -1,5 +1,6 @@ import ipcTypes from "../ipc.types"; import { + setSettings, setWatchedPaths, setWatcherStatus, } from "../redux/application/application.actions"; @@ -52,3 +53,7 @@ ipcRenderer.on( await UpsertEstimate(obj); } ); + +ipcRenderer.on(ipcTypes.default.store.response, (event, obj) => { + store.dispatch(setSettings(obj)); +}); diff --git a/src/redux/application/application.actions.js b/src/redux/application/application.actions.js index 6eb245d..ca179ea 100644 --- a/src/redux/application/application.actions.js +++ b/src/redux/application/application.actions.js @@ -38,3 +38,7 @@ export const setSelectedJobTargetPcSuccess = (pct) => ({ type: ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC_SUCCESS, payload: pct, }); +export const setSettings = (settingsObj) => ({ + type: ApplicationActionTypes.SET_SETTINGS, + payload: settingsObj, +}); diff --git a/src/redux/application/application.reducer.js b/src/redux/application/application.reducer.js index 2e25874..0814540 100644 --- a/src/redux/application/application.reducer.js +++ b/src/redux/application/application.reducer.js @@ -5,6 +5,7 @@ const INITIAL_STATE = { watcherError: null, selectedJobId: null, selectedJobTargetPc: 100, + settings: {}, }; const applicationReducer = (state = INITIAL_STATE, action) => { @@ -41,6 +42,9 @@ const applicationReducer = (state = INITIAL_STATE, action) => { }; case ApplicationActionTypes.SET_SELECTED_JOB_ID: return { ...state, selectedJobId: action.payload }; + case ApplicationActionTypes.SET_SETTINGS: + return { ...state, settings: { ...state.settings, ...action.payload } }; + default: return state; } diff --git a/src/redux/application/application.selectors.js b/src/redux/application/application.selectors.js index db993f7..beafc9f 100644 --- a/src/redux/application/application.selectors.js +++ b/src/redux/application/application.selectors.js @@ -26,3 +26,8 @@ export const selectSelectedJobTargetPc = createSelector( [selectApplication], (application) => application.selectedJobTargetPc ); + +export const selectSettings = createSelector( + [selectApplication], + (application) => application.settings +); diff --git a/src/redux/application/application.types.js b/src/redux/application/application.types.js index bac9945..3a73825 100644 --- a/src/redux/application/application.types.js +++ b/src/redux/application/application.types.js @@ -7,5 +7,6 @@ const ApplicationActionTypes = { SET_SELECTED_JOB_ID: "SET_SELECTED_JOB_ID", SET_SELECTED_JOB_TARGET_PC: "SET_SELECTED_JOB_TARGET_PC", SET_SELECTED_JOB_TARGET_PC_SUCCESS: "SET_SELECTED_JOB_TARGET_PC_SUCCESS", + SET_SETTINGS: "SET_SETTINGS", }; export default ApplicationActionTypes;