Add processing for upload dlock

This commit is contained in:
Senad Uka
2019-07-25 15:36:49 +02:00
parent 2db47e95e1
commit b0cadd14ea
13 changed files with 312 additions and 86 deletions

View File

@@ -4,7 +4,7 @@ import { Form } from 'semantic-ui-react';
import UnknownMapping from './UnknownMapping'; import UnknownMapping from './UnknownMapping';
import { uploadDoorLockData, fetchMappings } from '../../../store/actions'; import { uploadDoorLockData, fetchMappings, checkProcessing } from '../../../store/actions';
class FileUpload extends Component { class FileUpload extends Component {
constructor(props) { constructor(props) {
@@ -20,8 +20,9 @@ class FileUpload extends Component {
} }
componentDidMount() { componentDidMount() {
const { fetchMappings } = this.props; const { fetchMappings, checkProcessing } = this.props;
fetchMappings(); fetchMappings();
checkProcessing();
} }
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
@@ -36,6 +37,7 @@ class FileUpload extends Component {
this.setState({unknownMappings: filteredUnknownMappings}); this.setState({unknownMappings: filteredUnknownMappings});
} }
extractMappingFromFileName(fileName) { extractMappingFromFileName(fileName) {
const contentBetweenBracketsRegex = /\[(.*?)\]/; const contentBetweenBracketsRegex = /\[(.*?)\]/;
const rawContent = fileName.match(contentBetweenBracketsRegex)[1]; const rawContent = fileName.match(contentBetweenBracketsRegex)[1];
@@ -52,7 +54,11 @@ class FileUpload extends Component {
const { existingMappings } = mappings; const { existingMappings } = mappings;
if (existingMappings && Array.isArray(existingMappings)){
return existingMappings.find(mapping => (mapping.officeSlug === officeSlug) && (mapping.resourceSlug === resourceSlug)); return existingMappings.find(mapping => (mapping.officeSlug === officeSlug) && (mapping.resourceSlug === resourceSlug));
}else{
return false;
}
} }
onFileChange(event) { onFileChange(event) {
@@ -85,14 +91,24 @@ class FileUpload extends Component {
} }
}; };
refreshProcessingStatus = (event) => {
event.preventDefault();
const { checkProcessing } = this.props;
checkProcessing();
};
render() { render() {
const { pendingUpload } = this.props; const { pendingUpload, pendingStatus, processingStatus } = this.props;
const { unknownMappings, files } = this.state; const { unknownMappings, files } = this.state;
const uploadDisabled = pendingUpload || unknownMappings.length > 0 || !files; const processing = processingStatus && processingStatus.processing ? processingStatus.processing : false;
const uploadDisabled = pendingStatus || processing || pendingUpload || unknownMappings.length > 0 || !files;
return ( return (
<div> <div>
{processing && <p style={{ color: 'red' }}>Processing in progress. Please try again in a few minutes</p>}
{processing && <Form.Button onClick={this.refreshProcessingStatus}>Refresh</Form.Button>}
<Form.Input <Form.Input
fluid fluid
required required
@@ -121,11 +137,14 @@ const mapStateToProps = (state) => ({
pendingMappings: state.mappingsData.pending, pendingMappings: state.mappingsData.pending,
mappings: state.mappingsData.result, mappings: state.mappingsData.result,
addedMapping: state.addMapping.result, addedMapping: state.addMapping.result,
pendingStatus: state.checkProcessing.pending,
processingStatus: state.checkProcessing.result,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
uploadDoorLockData: (doorLockDataFiles) => uploadDoorLockData(dispatch, doorLockDataFiles), uploadDoorLockData: (doorLockDataFiles) => uploadDoorLockData(dispatch, doorLockDataFiles),
fetchMappings: () => fetchMappings(dispatch), fetchMappings: () => fetchMappings(dispatch),
checkProcessing: () => checkProcessing(dispatch),
}); });
export default connect(mapStateToProps, mapDispatchToProps)(FileUpload); export default connect(mapStateToProps, mapDispatchToProps)(FileUpload);

View File

@@ -2,7 +2,20 @@ import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Loader, Message, Tab, Label, Menu } from 'semantic-ui-react'; import { Loader, Message, Tab, Label, Menu } from 'semantic-ui-react';
import { checkProcessing } from '../../../store/actions';
class UploadResults extends Component { class UploadResults extends Component {
componentDidUpdate(prevProps, prevState, snapshot) {
const previousPending = prevProps.pending ? prevProps.pending : false;
const newPending = this.props.pending ? this.props.pending : false;
if (previousPending && !newPending){
const { checkProcessing } = this.props;
checkProcessing();
}
}
render(){ render(){
const {pending, result, error} = this.props; const {pending, result, error} = this.props;
@@ -80,7 +93,10 @@ const mapStateToProps = (state) => ({
pending: state.doorLockData.pending, pending: state.doorLockData.pending,
result: state.doorLockData.result, result: state.doorLockData.result,
error: state.doorLockData.error, error: state.doorLockData.error,
}); });
export default connect(mapStateToProps)(UploadResults); const mapDispatchToProps = (dispatch) => ({
checkProcessing: () => checkProcessing(dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(UploadResults);

View File

@@ -17,6 +17,9 @@ import {
ADD_FEES_TO_ORD_PENDING, ADD_FEES_TO_ORD_PENDING,
ADD_FEES_TO_ORD_SUCCESS, ADD_FEES_TO_ORD_SUCCESS,
ADD_FEES_TO_ORD_FAILED, ADD_FEES_TO_ORD_FAILED,
CHECK_PROCESSING_PENDING,
CHECK_PROCESSING_SUCCESS,
CHECK_PROCESSING_FAILED,
} from '../constants'; } from '../constants';
import API from '../../utilities/api'; import API from '../../utilities/api';
@@ -95,3 +98,14 @@ export const addFeesToOrd = (dispatch, dateRange, memberIds) => {
dispatch({type: ADD_FEES_TO_ORD_FAILED, payload: error.response}); dispatch({type: ADD_FEES_TO_ORD_FAILED, payload: error.response});
}); });
}; };
export const checkProcessing = (dispatch) => {
dispatch({type: CHECK_PROCESSING_PENDING});
API.get('integration/processing')
.then(response => {
dispatch({type: CHECK_PROCESSING_SUCCESS, payload: response.data});
})
.catch(error => {
dispatch({type: CHECK_PROCESSING_FAILED, payload: error.response});
});
};

View File

@@ -25,3 +25,7 @@ export const FETCH_MEMBER_INCIDENTS_FAILED = 'FETCH_MEMBER_INCIDENTS_FAILED';
export const ADD_FEES_TO_ORD_PENDING = 'ADD_FEES_TO_ORD_PENDING'; export const ADD_FEES_TO_ORD_PENDING = 'ADD_FEES_TO_ORD_PENDING';
export const ADD_FEES_TO_ORD_SUCCESS = 'ADD_FEES_TO_ORD_SUCCESS'; export const ADD_FEES_TO_ORD_SUCCESS = 'ADD_FEES_TO_ORD_SUCCESS';
export const ADD_FEES_TO_ORD_FAILED = 'ADD_FEES_TO_ORD_FAILED'; export const ADD_FEES_TO_ORD_FAILED = 'ADD_FEES_TO_ORD_FAILED';
export const CHECK_PROCESSING_PENDING = 'CHECK_PROCESSING_PENDING';
export const CHECK_PROCESSING_SUCCESS = 'CHECK_PROCESSING_SUCCESS';
export const CHECK_PROCESSING_FAILED = 'CHECK_PROCESSING_FAILED';

View File

@@ -0,0 +1,38 @@
import {
CHECK_PROCESSING_PENDING,
CHECK_PROCESSING_SUCCESS,
CHECK_PROCESSING_FAILED,
} from '../constants';
const initialState = {
pending: false,
result: null,
error: null,
};
export const checkProcessing = (state, action) => {
state = state || initialState;
action = action || {};
switch(action.type){
case CHECK_PROCESSING_PENDING:
return Object.assign({}, state, {
pending: true,
error: null,
});
case CHECK_PROCESSING_SUCCESS:
return Object.assign({}, state, {
pending: false,
result: action.payload,
error: null,
});
case CHECK_PROCESSING_FAILED:
return Object.assign({}, state, {
pending: false,
result: {},
error: action.payload,
});
default:
return state;
}
};

View File

@@ -7,6 +7,7 @@ import { incidentsReport } from './incidentsReportReducer';
import { membersList } from './membersListReducer'; import { membersList } from './membersListReducer';
import { memberIncidents} from './memberIncidentsReducer'; import { memberIncidents} from './memberIncidentsReducer';
import { addFeesStatus } from './addFeesToOrdReducer'; import { addFeesStatus } from './addFeesToOrdReducer';
import { checkProcessing } from './checkProcessingReducer';
export const rootReducer = combineReducers({ export const rootReducer = combineReducers({
doorLockData, doorLockData,
@@ -16,5 +17,6 @@ export const rootReducer = combineReducers({
membersList, membersList,
memberIncidents, memberIncidents,
addFeesStatus, addFeesStatus,
checkProcessing,
}); });

View File

@@ -62,6 +62,7 @@ const officeRnDAPIErrors = {
FAILED_TO_ADD_FEES: 'Failed to add fees in ORD', FAILED_TO_ADD_FEES: 'Failed to add fees in ORD',
}; };
const integrationServiceErrors = { const integrationServiceErrors = {
PROCESSING_TRY_AGAIN: 'Incident calculations are in progress. Please try again in a few minutes',
IMPORT_SUCCESSFUL_CALCULATION_FAILED: 'Import succeeded but fetching reservations and calculating charges failed', IMPORT_SUCCESSFUL_CALCULATION_FAILED: 'Import succeeded but fetching reservations and calculating charges failed',
FAILED_TO_SAVE_BOOKINGS: 'Failed to save booking reservations', FAILED_TO_SAVE_BOOKINGS: 'Failed to save booking reservations',
FAILED_TO_SAVE_DOOR_LOCK_ENTRIES: 'Failed to save door lock entries', FAILED_TO_SAVE_DOOR_LOCK_ENTRIES: 'Failed to save door lock entries',

View File

@@ -5,10 +5,16 @@ const { fetchAllBookings, writeBookingReservation } = require('../services/offic
const { calculateDoorLockCharges } = require('../services/integration/doorLockCharges'); const { calculateDoorLockCharges } = require('../services/integration/doorLockCharges');
const { integrationServiceErrors } = require('../constants/constants'); const { integrationServiceErrors } = require('../constants/constants');
const { checkBookingChanges } = require('../services/integration/checkBookingChange'); const { checkBookingChanges } = require('../services/integration/checkBookingChange');
const { checkIfProcessing, setStartProcessing, setDoneProcessing } = require('../services/integration/processingStatus');
const IncomingForm = require('formidable').IncomingForm; const IncomingForm = require('formidable').IncomingForm;
const uploadDoorLockData = (req, res) => { const uploadDoorLockData = (req, res) => {
checkIfProcessing()
.then((processing) => {
if (processing){
res.status(500).send(integrationServiceErrors.PROCESSING_TRY_AGAIN);
}else{
const form = new IncomingForm(); const form = new IncomingForm();
const fileParsers = []; const fileParsers = [];
@@ -42,25 +48,42 @@ const uploadDoorLockData = (req, res) => {
parsedData.forEach((entry) => asyncWriteJobs.push(writeDoorLockEvent(entry))); parsedData.forEach((entry) => asyncWriteJobs.push(writeDoorLockEvent(entry)));
Promise.all(asyncWriteJobs) Promise.all(asyncWriteJobs)
.then(() => {
checkBookingChanges()
.then(() => {
calculateDoorLockCharges()
.then(() => { .then(() => {
res.json({ res.json({
parsedData, parsedData,
parserErrors, parserErrors,
unknownMembers unknownMembers
}); });
setStartProcessing()
.then(() => {
checkBookingChanges()
.then(() => {
calculateDoorLockCharges()
.then(() => {
setDoneProcessing()
.catch((error) => {
console.log('Error in process done indication');
console.log(error);
})
}) })
.catch((error) => { .catch((error) => {
console.log('Error : ', error); console.log('Error : ', error);
res.status(500).send(integrationServiceErrors.IMPORT_SUCCESSFUL_CALCULATION_FAILED); setDoneProcessing();
// res.status(500).send(integrationServiceErrors.IMPORT_SUCCESSFUL_CALCULATION_FAILED);
});
})
.catch((error) => {
console.log('Error : ', error);
setDoneProcessing();
// res.status(500).send(integrationServiceErrors.IMPORT_SUCCESSFUL_CALCULATION_FAILED);
}); });
}) })
.catch((error) => { .catch((error) => {
console.log('Error : ', error); console.log('Error in processing indicator');
res.status(500).send(integrationServiceErrors.IMPORT_SUCCESSFUL_CALCULATION_FAILED); console.log(error);
setDoneProcessing();
}); });
}) })
.catch((error) => { .catch((error) => {
@@ -95,6 +118,13 @@ const uploadDoorLockData = (req, res) => {
}); });
}); });
form.parse(req); form.parse(req);
}
})
.catch((error) => {
console.log('Error while checking processing : ');
console.log(error);
res.status(500).send('')
});
}; };
module.exports = { module.exports = {

View File

@@ -5,6 +5,7 @@ const { getAllIncidents } = require('../services/integration/reports');
const { getMembersFeesForDateRange } = require('../services/integration/invoiceIntegration'); const { getMembersFeesForDateRange } = require('../services/integration/invoiceIntegration');
const { deleteFeesFromORD, addFeesToORD } = require('../services/officeRnD/fees'); const { deleteFeesFromORD, addFeesToORD } = require('../services/officeRnD/fees');
const { checkBookingChanges } = require('../services/integration/checkBookingChange'); const { checkBookingChanges } = require('../services/integration/checkBookingChange');
const { checkIfProcessing } = require('../services/integration/processingStatus');
const getKnownOfficeResourceMappings = (req, res) => { const getKnownOfficeResourceMappings = (req, res) => {
const dataToFetch = [getMappingsFromDatabase(), fetchOffices(), fetchResources() ]; const dataToFetch = [getMappingsFromDatabase(), fetchOffices(), fetchResources() ];
@@ -111,10 +112,25 @@ const addFees = (req, res) => {
} }
}; };
const checkProcessingStatus = (req, res) => {
checkIfProcessing()
.then((processing) => {
res.send({
processing
})
})
.catch((error) => {
console.log('Error checking if processing ');
console.log(error);
res.status(500).send(error);
});
};
module.exports = { module.exports = {
getKnownOfficeResourceMappings, getKnownOfficeResourceMappings,
addNewMapping, addNewMapping,
getAllIncidentsController, getAllIncidentsController,
getMemberIncidents, getMemberIncidents,
addFees, addFees,
checkProcessingStatus,
}; };

View File

@@ -0,0 +1,26 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('processings', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
processing: Sequelize.BOOLEAN,
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('processings');
}
};

11
models/processing.js Normal file
View File

@@ -0,0 +1,11 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const processing = sequelize.define('processing', {
processing: DataTypes.BOOLEAN,
}, {});
processing.associate = function(models) {
// associations can be defined here
};
return processing;
};

View File

@@ -8,7 +8,8 @@ const {
addNewMapping, addNewMapping,
getAllIncidentsController, getAllIncidentsController,
getMemberIncidents, getMemberIncidents,
addFees addFees,
checkProcessingStatus,
} = require('../controllers/integration'); } = require('../controllers/integration');
const { calculateDoorLockCharges } = require('../services/integration/doorLockCharges'); const { calculateDoorLockCharges } = require('../services/integration/doorLockCharges');
@@ -29,6 +30,8 @@ router.get('/officeRnD/membersList', fetchMembersList);
router.post('/integration/addFees', addFees); router.post('/integration/addFees', addFees);
router.get('/integration/processing', checkProcessingStatus);
// temporary route, manually trigger door lock charge calculations // temporary route, manually trigger door lock charge calculations
router.get('/calculate', (req, res) => { calculateDoorLockCharges(); res.send();}); router.get('/calculate', (req, res) => { calculateDoorLockCharges(); res.send();});

View File

@@ -0,0 +1,46 @@
'use strict';
const db = require('../../models/index');
const setProcessingValue = (value) => {
return new Promise((resolve, reject) => {
const values = {processing: value};
db.processing.update(values, {where:{}})
.then(() => {
resolve(true);
})
.catch((error) => reject(error));
});
};
const setStartProcessing = () => {
return setProcessingValue(true);
};
const setDoneProcessing = () => {
return setProcessingValue(false);
};
const checkIfProcessing = () => {
return new Promise((resolve, reject) => {
db.processing.findAll()
.then((results) => {
if (results && results.length > 0){
resolve(results[0].getDataValue('processing'));
}else{
db.processing.bulkCreate([{processing: false}])
.then(() => {
resolve(false);
})
.catch((error) => reject(error));
}
})
.catch((error) => reject(error));
});
};
module.exports = {
setStartProcessing,
setDoneProcessing,
checkIfProcessing
};