display upload errors, unknown members
This commit is contained in:
@@ -31,6 +31,7 @@ class FileUpload extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { pending } = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
@@ -41,14 +42,18 @@ class FileUpload extends Component {
|
|||||||
accept=".csv"
|
accept=".csv"
|
||||||
onChange={this.onFileChange}
|
onChange={this.onFileChange}
|
||||||
/>
|
/>
|
||||||
<Form.Button onClick={this.onUploadClick}>Upload</Form.Button>
|
<Form.Button onClick={this.onUploadClick} disabled={pending} >Upload</Form.Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
pending: state.doorLockData.pending,
|
||||||
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
uploadDoorLockData: (doorLockDataFile) => uploadDoorLockData(dispatch, doorLockDataFile)
|
uploadDoorLockData: (doorLockDataFile) => uploadDoorLockData(dispatch, doorLockDataFile)
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(FileUpload);
|
export default connect(mapStateToProps, mapDispatchToProps)(FileUpload);
|
||||||
|
|||||||
@@ -1,13 +1,58 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Loader, Message } from 'semantic-ui-react';
|
import { Loader, Message, Tab, Label, Menu } from 'semantic-ui-react';
|
||||||
|
|
||||||
class UploadResults extends Component {
|
class UploadResults extends Component {
|
||||||
render(){
|
render(){
|
||||||
const {pending, result, error} = this.props;
|
const {pending, result, error} = this.props;
|
||||||
|
|
||||||
const parserErrors = result && result.parserErrors && result.parserErrors.length > 0 ? result.parserErrors : null;
|
const parsedEntries = result && result.parsedData ? result.parsedData : [];
|
||||||
const parsedDataCount = result && result.parsedData && result.parsedData.length > 0 ? result.parsedData.length : null;
|
const errorEntries = result && result.parserErrors ? result.parserErrors : [];
|
||||||
|
const unknownMembers = result && result.unknownMembers ? result.unknownMembers : [];
|
||||||
|
|
||||||
|
const renderParsedEntriesTab = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<br/>
|
||||||
|
<Message positive>
|
||||||
|
<p>{parsedEntries.length} entries successfully parsed</p>
|
||||||
|
</Message>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderErrorTabResults = (results) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<br/>
|
||||||
|
{
|
||||||
|
results.map((entry, index) => {
|
||||||
|
return (
|
||||||
|
<div key={`error-${entry.error}-${index}`}>
|
||||||
|
<br/>
|
||||||
|
<Message negative>
|
||||||
|
<Message.Header>{entry.error}</Message.Header>
|
||||||
|
<p>{JSON.stringify(entry.details)}</p>
|
||||||
|
<p>File : {entry.file}</p>
|
||||||
|
</Message>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedEntriesTabTitle = (<Menu.Item key="parsed-entries">Parsed Entries<Label>{parsedEntries.length}</Label></Menu.Item>);
|
||||||
|
const errorEntriesTabTitle = (<Menu.Item key="error-entries">Error Entries<Label>{errorEntries.length}</Label></Menu.Item>);
|
||||||
|
const unknownMembersTabTitle = (<Menu.Item key="unknown-members">Unknown Members<Label>{unknownMembers.length}</Label></Menu.Item>);
|
||||||
|
|
||||||
|
const panes = [
|
||||||
|
{menuItem: parsedEntriesTabTitle, render: renderParsedEntriesTab},
|
||||||
|
{menuItem: errorEntriesTabTitle, render: () => renderErrorTabResults(errorEntries)},
|
||||||
|
{menuItem: unknownMembersTabTitle, render: () => renderErrorTabResults(unknownMembers)}
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -16,36 +61,16 @@ class UploadResults extends Component {
|
|||||||
}
|
}
|
||||||
<br/>
|
<br/>
|
||||||
{
|
{
|
||||||
error &&
|
!pending && !error && result &&
|
||||||
<Message negative>
|
<Tab panes={panes}/>
|
||||||
<Message.Header>Upload failed</Message.Header>
|
|
||||||
<p>There was error uploading file</p>
|
|
||||||
</Message>
|
|
||||||
}
|
}
|
||||||
<br/>
|
|
||||||
{
|
{
|
||||||
!error && parsedDataCount &&
|
!pending && error &&
|
||||||
<Message positive>
|
<Message>
|
||||||
<Message.Header>Upload complete</Message.Header>
|
<Message.Header>Upload Failed</Message.Header>
|
||||||
<p>{parsedDataCount} entries successfully inserted</p>
|
<p>{error.data}</p>
|
||||||
{parserErrors && <p style={{ color: 'red' }}>Some entries could not be parsed. Details are shown below</p>}
|
|
||||||
</Message>
|
</Message>
|
||||||
}
|
}
|
||||||
<br/>
|
|
||||||
{
|
|
||||||
!error && parserErrors && parserErrors.map((parserError, index) => {
|
|
||||||
return (
|
|
||||||
<div key={`message-${index}`}>
|
|
||||||
<Message negative>
|
|
||||||
<Message.Header>{parserError.error}</Message.Header>
|
|
||||||
<p>{JSON.stringify(parserError.details)}</p>
|
|
||||||
<p>File : {parserError.file}</p>
|
|
||||||
</Message>
|
|
||||||
<br/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
pending: false,
|
pending: false,
|
||||||
result: {},
|
result: null,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ const csvParserErrors = {
|
|||||||
UNKNOWN_COLUMN: 'Unknown column',
|
UNKNOWN_COLUMN: 'Unknown column',
|
||||||
INVALID_ENTRY_EXPECTED_USER: 'Invalid entry type. Expected user entry type',
|
INVALID_ENTRY_EXPECTED_USER: 'Invalid entry type. Expected user entry type',
|
||||||
INVALID_ENTRY_EXPECTED_PASSAGE_MODE: 'Invalid entry type. Expected enable/disable passage mode',
|
INVALID_ENTRY_EXPECTED_PASSAGE_MODE: 'Invalid entry type. Expected enable/disable passage mode',
|
||||||
|
UNKNOWN_MEMBER: 'Member is not registered in OfficeRnD system',
|
||||||
|
};
|
||||||
|
|
||||||
|
const officeRnDAPIErrors = {
|
||||||
|
FAILED_TO_FETCH_MEMBERS: 'Failed to fetch members',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -21,4 +26,5 @@ module.exports = {
|
|||||||
USER_LOCKED_DOOR,
|
USER_LOCKED_DOOR,
|
||||||
USER_UNLOCKED_DOOR,
|
USER_UNLOCKED_DOOR,
|
||||||
csvParserErrors,
|
csvParserErrors,
|
||||||
|
officeRnDAPIErrors,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { parseDoorLockDataFile, writeDoorLockEvent } = require("../services/doorLock");
|
const { parseDoorLockDataFile, writeDoorLockEvent } = require("../services/doorLock");
|
||||||
|
const { officeRnDAPIErrors } = require('../constants/constants');
|
||||||
|
|
||||||
const IncomingForm = require('formidable').IncomingForm;
|
const IncomingForm = require('formidable').IncomingForm;
|
||||||
|
|
||||||
|
|
||||||
const uploadDoorLockData = (req, res) => {
|
const uploadDoorLockData = (req, res) => {
|
||||||
const form = new IncomingForm();
|
const form = new IncomingForm();
|
||||||
const parsingResults = [];
|
const parsingResults = [];
|
||||||
@@ -20,24 +20,26 @@ const uploadDoorLockData = (req, res) => {
|
|||||||
.then((parserResults) => {
|
.then((parserResults) => {
|
||||||
const parsedData = [];
|
const parsedData = [];
|
||||||
const parserErrors = [];
|
const parserErrors = [];
|
||||||
|
const unknownMembers = [];
|
||||||
|
|
||||||
parserResults.forEach((parserResult) => {
|
parserResults.forEach((parserResult) => {
|
||||||
parsedData.push(...parserResult.parsedData);
|
parsedData.push(...parserResult.parsedData);
|
||||||
parserErrors.push(...parserResult.errors);
|
parserErrors.push(...parserResult.errors);
|
||||||
|
unknownMembers.push(...parserResult.unknownMembers);
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
parsedData,
|
parsedData,
|
||||||
parserErrors,
|
parserErrors,
|
||||||
|
unknownMembers
|
||||||
});
|
});
|
||||||
|
|
||||||
parsedData.forEach((entry) => {
|
parsedData.forEach((entry) => {
|
||||||
writeDoorLockEvent(entry);
|
writeDoorLockEvent(entry);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(() => {
|
||||||
console.log(error);
|
res.status(500).send(officeRnDAPIErrors.FAILED_TO_FETCH_MEMBERS);
|
||||||
res.json({result: 'error'});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
12
helpers/api.js
Normal file
12
helpers/api.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const officeRnDToken = process.env.OFFICE_RnD_TOKEN;
|
||||||
|
|
||||||
|
const API = axios.create({
|
||||||
|
baseURL: 'https://app.officernd.com/api/v1/organizations/sima-space-test-environment',
|
||||||
|
headers: {'Authorization': `Bearer ${officeRnDToken}`}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
API,
|
||||||
|
};
|
||||||
@@ -9,8 +9,9 @@ module.exports = {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
type: Sequelize.INTEGER
|
type: Sequelize.INTEGER
|
||||||
},
|
},
|
||||||
memberName: Sequelize.STRING,
|
memberName: Sequelize.TEXT,
|
||||||
memberNumber: Sequelize.INTEGER,
|
memberNumber: Sequelize.INTEGER,
|
||||||
|
memberId: Sequelize.TEXT,
|
||||||
event: {
|
event: {
|
||||||
type: Sequelize.ENUM,
|
type: Sequelize.ENUM,
|
||||||
values: ['locked', 'unlocked']
|
values: ['locked', 'unlocked']
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ const { USER_LOCKED_DOOR, USER_UNLOCKED_DOOR } = require('../constants/constants
|
|||||||
|
|
||||||
module.exports = (sequelize, DataTypes) => {
|
module.exports = (sequelize, DataTypes) => {
|
||||||
const doorLockEvent = sequelize.define('doorLockEvent', {
|
const doorLockEvent = sequelize.define('doorLockEvent', {
|
||||||
memberName: DataTypes.STRING,
|
memberName: DataTypes.TEXT,
|
||||||
memberNumber: DataTypes.INTEGER,
|
memberNumber: DataTypes.INTEGER,
|
||||||
|
memberId: DataTypes.TEXT,
|
||||||
event: {
|
event: {
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
values: [USER_LOCKED_DOOR, USER_UNLOCKED_DOOR]
|
values: [USER_LOCKED_DOOR, USER_UNLOCKED_DOOR]
|
||||||
|
|||||||
30
package-lock.json
generated
30
package-lock.json
generated
@@ -119,6 +119,15 @@
|
|||||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||||
|
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.3.0",
|
||||||
|
"is-buffer": "^1.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-runtime": {
|
"babel-runtime": {
|
||||||
"version": "6.26.0",
|
"version": "6.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||||
@@ -1118,6 +1127,24 @@
|
|||||||
"locate-path": "^3.0.0"
|
"locate-path": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^3.2.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||||
@@ -1955,8 +1982,7 @@
|
|||||||
"is-buffer": {
|
"is-buffer": {
|
||||||
"version": "1.1.6",
|
"version": "1.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"is-ci": {
|
"is-ci": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"node": "11.12.x"
|
"node": "11.12.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.18.0",
|
||||||
"csv-parser": "^2.3.0",
|
"csv-parser": "^2.3.0",
|
||||||
"dotenv": "^8.0.0",
|
"dotenv": "^8.0.0",
|
||||||
"express": "^4.17.0",
|
"express": "^4.17.0",
|
||||||
|
|||||||
@@ -14,84 +14,110 @@ const {
|
|||||||
csvParserErrors,
|
csvParserErrors,
|
||||||
} = require('../constants/constants');
|
} = require('../constants/constants');
|
||||||
|
|
||||||
|
const { fetchAllMembers, findMember } = require('../services/officeRnD/members');
|
||||||
|
|
||||||
|
|
||||||
const parseDoorLockDataFile = (file) => {
|
const parseDoorLockDataFile = (file) => {
|
||||||
return new Promise ((resolve, reject) => {
|
return new Promise ((resolve, reject) => {
|
||||||
const results = [];
|
const results = [];
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
const unknownMembers = [];
|
||||||
let isValidFile = true;
|
let isValidFile = true;
|
||||||
|
|
||||||
fs.createReadStream(file.path)
|
fetchAllMembers()
|
||||||
.pipe(csv({
|
.then(() => {
|
||||||
mapHeaders: ({ header, index }) => header.trim().toLowerCase(),
|
fs.createReadStream(file.path)
|
||||||
mapValues: ({ header, index, value }) => value.trim()
|
.pipe(csv({
|
||||||
}))
|
mapHeaders: ({ header, index }) => header.trim().toLowerCase(),
|
||||||
.on('headers', (headers) => {
|
mapValues: ({ header, index, value }) => value.trim()
|
||||||
headers.forEach((header) => {
|
}))
|
||||||
if (!VALID_CSV_HEADERS.includes(header.trim())){
|
.on('headers', (headers) => {
|
||||||
isValidFile = false;
|
headers.forEach((header) => {
|
||||||
console.log('INVALID HEADER');
|
if (!VALID_CSV_HEADERS.includes(header.trim())){
|
||||||
errors.push({
|
isValidFile = false;
|
||||||
error: csvParserErrors.UNKNOWN_COLUMN,
|
console.log('INVALID HEADER');
|
||||||
details: header,
|
errors.push({
|
||||||
file: file.name,
|
error: csvParserErrors.UNKNOWN_COLUMN,
|
||||||
|
details: header,
|
||||||
|
file: file.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
});
|
.on('data', (data) => {
|
||||||
})
|
if (!isValidFile) {
|
||||||
.on('data', (data) => {
|
return;
|
||||||
if (!isValidFile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventType = data.event.trim();
|
|
||||||
if ([USER_ENTRY_EVENT, ENABLE_PASSAGE_MODE, DISABLE_PASSAGE_MODE].includes(eventType)){
|
|
||||||
results.push(data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on('end', () => {
|
|
||||||
const parsedData = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < results.length){
|
|
||||||
//Verify pair
|
|
||||||
//First entry should be user entry and second should be enable / disable passage
|
|
||||||
const firstEntry = results[i];
|
|
||||||
const secondEntry = results[i+1];
|
|
||||||
|
|
||||||
|
|
||||||
if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){
|
|
||||||
if (secondEntry && (secondEntry.event === ENABLE_PASSAGE_MODE || secondEntry.event === DISABLE_PASSAGE_MODE)){
|
|
||||||
const event = (secondEntry.event === ENABLE_PASSAGE_MODE) ? USER_UNLOCKED_DOOR : USER_LOCKED_DOOR;
|
|
||||||
const entryData = {
|
|
||||||
memberName: firstEntry.name,
|
|
||||||
memberNumber: firstEntry['user no'],
|
|
||||||
date: firstEntry.date,
|
|
||||||
time: firstEntry.time,
|
|
||||||
event,
|
|
||||||
};
|
|
||||||
parsedData.push(entryData);
|
|
||||||
i+=2;
|
|
||||||
} else {
|
|
||||||
errors.push({
|
|
||||||
error: csvParserErrors.INVALID_ENTRY_EXPECTED_PASSAGE_MODE,
|
|
||||||
details: secondEntry || 'Last row in file',
|
|
||||||
file: file.name,
|
|
||||||
});
|
|
||||||
i+=1;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
errors.push({
|
const eventType = data.event.trim();
|
||||||
error: csvParserErrors.INVALID_ENTRY_EXPECTED_USER,
|
if ([USER_ENTRY_EVENT, ENABLE_PASSAGE_MODE, DISABLE_PASSAGE_MODE].includes(eventType)){
|
||||||
details: firstEntry,
|
results.push(data);
|
||||||
file: file.name,
|
}
|
||||||
|
})
|
||||||
|
.on('end', () => {
|
||||||
|
const parsedData = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < results.length){
|
||||||
|
//Verify pair
|
||||||
|
//First entry should be user entry and second should be enable / disable passage
|
||||||
|
const firstEntry = results[i];
|
||||||
|
const secondEntry = results[i+1];
|
||||||
|
|
||||||
|
if (firstEntry && (firstEntry.event === USER_ENTRY_EVENT)){
|
||||||
|
if (secondEntry && (secondEntry.event === ENABLE_PASSAGE_MODE || secondEntry.event === DISABLE_PASSAGE_MODE)){
|
||||||
|
const event = (secondEntry.event === ENABLE_PASSAGE_MODE) ? USER_UNLOCKED_DOOR : USER_LOCKED_DOOR;
|
||||||
|
const memberObject = findMember(firstEntry.name);
|
||||||
|
|
||||||
|
//Verify that member is registered in OfficeRnD system
|
||||||
|
if (memberObject){
|
||||||
|
const entryData = {
|
||||||
|
memberName: firstEntry.name,
|
||||||
|
memberNumber: firstEntry['user no'],
|
||||||
|
memberId: memberObject.memberId,
|
||||||
|
date: firstEntry.date,
|
||||||
|
time: firstEntry.time,
|
||||||
|
event,
|
||||||
|
};
|
||||||
|
|
||||||
|
parsedData.push(entryData);
|
||||||
|
} else {
|
||||||
|
//Check if member is already labeled as unknown
|
||||||
|
const unknownMember = unknownMembers.find((member) => member.details === firstEntry.name);
|
||||||
|
if (!unknownMember){
|
||||||
|
unknownMembers.push({
|
||||||
|
error: csvParserErrors.UNKNOWN_MEMBER,
|
||||||
|
details: firstEntry.name,
|
||||||
|
file: file.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i+=2;
|
||||||
|
} else {
|
||||||
|
errors.push({
|
||||||
|
error: csvParserErrors.INVALID_ENTRY_EXPECTED_PASSAGE_MODE,
|
||||||
|
details: secondEntry || 'Last row in file',
|
||||||
|
file: file.name,
|
||||||
|
});
|
||||||
|
i+=1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors.push({
|
||||||
|
error: csvParserErrors.INVALID_ENTRY_EXPECTED_USER,
|
||||||
|
details: firstEntry,
|
||||||
|
file: file.name,
|
||||||
|
});
|
||||||
|
i+=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
parsedData,
|
||||||
|
unknownMembers,
|
||||||
|
errors
|
||||||
});
|
});
|
||||||
i+=1;
|
});
|
||||||
}
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
resolve({
|
reject(error);
|
||||||
parsedData,
|
|
||||||
errors
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
33
services/officeRnD/members.js
Normal file
33
services/officeRnD/members.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { API } = require('../../helpers/api');
|
||||||
|
|
||||||
|
const membersList = [];
|
||||||
|
|
||||||
|
const fetchAllMembers = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
API.get('/members')
|
||||||
|
.then((result) => {
|
||||||
|
const members = result.data || [];
|
||||||
|
members.forEach((member) => {
|
||||||
|
membersList.push({
|
||||||
|
name: member.name,
|
||||||
|
memberId: member['_id'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const findMember = (memberName) => {
|
||||||
|
return membersList.find((member) => member.name === memberName);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
fetchAllMembers,
|
||||||
|
findMember,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user