diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b7c063f..452c13d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,6 +15,18 @@ env:
docker_repository: nlpsandbox/phi-deidentifier-app
jobs:
+ build:
+ env:
+ working-directory: ./client
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install modules
+ working-directory: ${{env.working-directory}}
+ run: npm ci
+ - name: Run ESLint
+ working-directory: ${{env.working-directory}}
+ run: npx eslint
docker:
# needs: [test]
runs-on: ubuntu-latest
diff --git a/client/.eslintrc.yml b/client/.eslintrc.yml
new file mode 100644
index 0000000..3a6ec77
--- /dev/null
+++ b/client/.eslintrc.yml
@@ -0,0 +1,21 @@
+env:
+ browser: true
+ es2021: true
+extends:
+ - 'plugin:react/recommended'
+ - google
+parser: '@typescript-eslint/parser'
+parserOptions:
+ ecmaFeatures:
+ jsx: true
+ ecmaVersion: 12
+ sourceType: module
+plugins:
+ - react
+ - '@typescript-eslint'
+rules:
+ no-invalid-this: off
+ require-jsdoc: off
+ indent:
+ - error
+ - 2
diff --git a/client/package-lock.json b/client/package-lock.json
index f8e7b72..ce1be9a 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -2579,18 +2579,70 @@
"integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw=="
},
"@typescript-eslint/eslint-plugin": {
- "version": "4.15.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.2.tgz",
- "integrity": "sha512-uiQQeu9tWl3f1+oK0yoAv9lt/KXO24iafxgQTkIYO/kitruILGx3uH+QtIAHqxFV+yIsdnJH+alel9KuE3J15Q==",
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.17.0.tgz",
+ "integrity": "sha512-/fKFDcoHg8oNan39IKFOb5WmV7oWhQe1K6CDaAVfJaNWEhmfqlA24g+u1lqU5bMH7zuNasfMId4LaYWC5ijRLw==",
"requires": {
- "@typescript-eslint/experimental-utils": "4.15.2",
- "@typescript-eslint/scope-manager": "4.15.2",
+ "@typescript-eslint/experimental-utils": "4.17.0",
+ "@typescript-eslint/scope-manager": "4.17.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
"regexpp": "^3.0.0",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
+ },
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.17.0.tgz",
+ "integrity": "sha512-ZR2NIUbnIBj+LGqCFGQ9yk2EBQrpVVFOh9/Kd0Lm6gLpSAcCuLLe5lUCibKGCqyH9HPwYC0GIJce2O1i8VYmWA==",
+ "requires": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/scope-manager": "4.17.0",
+ "@typescript-eslint/types": "4.17.0",
+ "@typescript-eslint/typescript-estree": "4.17.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^2.0.0"
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz",
+ "integrity": "sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw==",
+ "requires": {
+ "@typescript-eslint/types": "4.17.0",
+ "@typescript-eslint/visitor-keys": "4.17.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.17.0.tgz",
+ "integrity": "sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g=="
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz",
+ "integrity": "sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ==",
+ "requires": {
+ "@typescript-eslint/types": "4.17.0",
+ "@typescript-eslint/visitor-keys": "4.17.0",
+ "debug": "^4.1.1",
+ "globby": "^11.0.1",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.2",
+ "tsutils": "^3.17.1"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz",
+ "integrity": "sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ==",
+ "requires": {
+ "@typescript-eslint/types": "4.17.0",
+ "eslint-visitor-keys": "^2.0.0"
+ }
+ }
}
},
"@typescript-eslint/experimental-utils": {
@@ -2607,14 +2659,53 @@
}
},
"@typescript-eslint/parser": {
- "version": "4.15.2",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.2.tgz",
- "integrity": "sha512-SHeF8xbsC6z2FKXsaTb1tBCf0QZsjJ94H6Bo51Y1aVEZ4XAefaw5ZAilMoDPlGghe+qtq7XdTiDlGfVTOmvA+Q==",
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.17.0.tgz",
+ "integrity": "sha512-KYdksiZQ0N1t+6qpnl6JeK9ycCFprS9xBAiIrw4gSphqONt8wydBw4BXJi3C11ywZmyHulvMaLjWsxDjUSDwAw==",
"requires": {
- "@typescript-eslint/scope-manager": "4.15.2",
- "@typescript-eslint/types": "4.15.2",
- "@typescript-eslint/typescript-estree": "4.15.2",
+ "@typescript-eslint/scope-manager": "4.17.0",
+ "@typescript-eslint/types": "4.17.0",
+ "@typescript-eslint/typescript-estree": "4.17.0",
"debug": "^4.1.1"
+ },
+ "dependencies": {
+ "@typescript-eslint/scope-manager": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz",
+ "integrity": "sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw==",
+ "requires": {
+ "@typescript-eslint/types": "4.17.0",
+ "@typescript-eslint/visitor-keys": "4.17.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.17.0.tgz",
+ "integrity": "sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g=="
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz",
+ "integrity": "sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ==",
+ "requires": {
+ "@typescript-eslint/types": "4.17.0",
+ "@typescript-eslint/visitor-keys": "4.17.0",
+ "debug": "^4.1.1",
+ "globby": "^11.0.1",
+ "is-glob": "^4.0.1",
+ "semver": "^7.3.2",
+ "tsutils": "^3.17.1"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "4.17.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz",
+ "integrity": "sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ==",
+ "requires": {
+ "@typescript-eslint/types": "4.17.0",
+ "eslint-visitor-keys": "^2.0.0"
+ }
+ }
}
},
"@typescript-eslint/scope-manager": {
@@ -5852,6 +5943,12 @@
}
}
},
+ "eslint-config-google": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz",
+ "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==",
+ "dev": true
+ },
"eslint-config-react-app": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
@@ -14621,9 +14718,9 @@
},
"dependencies": {
"ajv": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.1.tgz",
- "integrity": "sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ==",
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz",
+ "integrity": "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -15341,9 +15438,9 @@
"optional": true
},
"v8-compile-cache": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
- "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q=="
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="
},
"v8-to-istanbul": {
"version": "7.1.0",
diff --git a/client/package.json b/client/package.json
index 48ac4b0..8220822 100644
--- a/client/package.json
+++ b/client/package.json
@@ -9,6 +9,7 @@
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.1",
+ "prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
@@ -39,5 +40,12 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "@typescript-eslint/eslint-plugin": "^4.17.0",
+ "@typescript-eslint/parser": "^4.17.0",
+ "eslint": "^7.21.0",
+ "eslint-config-google": "^0.14.0",
+ "eslint-plugin-react": "^7.22.0"
}
}
diff --git a/client/src/App.test.js b/client/src/App.test.js
index 1f03afe..a2b8750 100644
--- a/client/src/App.test.js
+++ b/client/src/App.test.js
@@ -1,4 +1,5 @@
-import { render, screen } from '@testing-library/react';
+import {render, screen} from '@testing-library/react';
+import {React} from 'react';
import App from './App';
test('renders learn react link', () => {
diff --git a/client/src/components/AnnotationView.js b/client/src/components/AnnotationView.js
index 68d7e9e..f345dce 100644
--- a/client/src/components/AnnotationView.js
+++ b/client/src/components/AnnotationView.js
@@ -1,39 +1,56 @@
-import { Paper, Zoom } from '@material-ui/core';
-import { DataGrid } from '@material-ui/data-grid';
+import {Paper} from '@material-ui/core';
+import {DataGrid} from '@material-ui/data-grid';
import React from 'react';
-import { deidentificationStates } from './DeidentifiedText';
-
-export function AnnotationView(props) {
+import {deidentificationStates} from './DeidentifiedText';
+import PropTypes from 'prop-types';
+function AnnotationView(props) {
const types = [
- {type: "text_date", name: "Date", key: "textDateAnnotations"},
- {type: "text_physical_address", name: "Physical Address", key: "textPhysicalAddressAnnotations"},
- {type: "text_person_name", name: "Person Name", key: "textPersonNameAnnotations"}
- ]
+ {type: 'text_date', name: 'Date', key: 'textDateAnnotations'},
+ {type: 'text_physical_address', name: 'Physical Address', key:
+ 'textPhysicalAddressAnnotations'},
+ {type: 'text_person_name', name: 'Person Name', key:
+ 'textPersonNameAnnotations'},
+ ];
let allAnnotations;
- if (props.annotations === deidentificationStates.EMPTY || props.annotations === deidentificationStates.LOADING || props.annotations === deidentificationStates.ERROR) {
- allAnnotations = []
+ if (props.annotations === deidentificationStates.EMPTY ||
+ props.annotations === deidentificationStates.LOADING ||
+ props.annotations === deidentificationStates.ERROR) {
+ allAnnotations = [];
} else {
allAnnotations = types.map(
(type) => props.annotations[type.key].map((annotation, index) => {
- return {type: type.name, id: String(type.type)+'_'+String(index), ...annotation};
- })
+ return {
+ type: type.name,
+ id: String(type.type)+'_'+String(index),
+ ...annotation,
+ };
+ }),
).flat();
}
const columns = [
- {field: 'id', headerName: "ID", hide: true},
- {field: 'type', headerName: "Type", width: 125},
- {field: 'text', headerName: "Text", width: 130},
- {field: 'start', headerName: "Start", width: 90},
- {field: 'length', headerName: "Length", width: 100},
- {field: 'confidence', headerName: "Confidence", width: 130}
- ]
+ {field: 'id', headerName: 'ID', hide: true},
+ {field: 'type', headerName: 'Type', width: 125},
+ {field: 'text', headerName: 'Text', width: 130},
+ {field: 'start', headerName: 'Start', width: 90},
+ {field: 'length', headerName: 'Length', width: 100},
+ {field: 'confidence', headerName: 'Confidence', width: 130},
+ ];
return (
-
+
);
-}
\ No newline at end of file
+}
+
+AnnotationView.propTypes = {
+ annotations: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number,
+ ]),
+};
+
+export default AnnotationView;
diff --git a/client/src/components/App.js b/client/src/components/App.js
index 17c336d..9b70ec6 100644
--- a/client/src/components/App.js
+++ b/client/src/components/App.js
@@ -1,50 +1,60 @@
import React from 'react';
+import PropTypes from 'prop-types';
import Config from '../config';
-import { DeidentifiedNoteApi, ToolApi } from '../apis';
-import { DeidentifyRequestFromJSON } from '../models';
-import { Configuration } from '../runtime';
-import { encodeString, decodeString } from '../stringSmuggler';
+import {DeidentifiedNoteApi, ToolApi} from '../apis';
+import {DeidentifyRequestFromJSON} from '../models';
+import {Configuration} from '../runtime';
+import {encodeString, decodeString} from '../stringSmuggler';
-import { withStyles } from '@material-ui/core/styles';
-import { AppBar, Box, Button, IconButton, Paper, Toolbar, Grid, Typography, TextField, Fab } from '@material-ui/core';
+import {withStyles} from '@material-ui/core/styles';
+import {AppBar, Box, Button, IconButton, Paper, Toolbar, Grid, Typography,
+ TextField, Fab} from '@material-ui/core';
import InfoIcon from '@material-ui/icons/Info';
import AddIcon from '@material-ui/icons/Add';
-import { DeidentifiedText, deidentificationStates } from './DeidentifiedText';
-import { InfoDialog } from './InfoDialog';
-import { DeidentificationConfigForm } from './DeidentificationConfigForm';
-import { AnnotationView } from './AnnotationView';
+import {DeidentifiedText, deidentificationStates} from './DeidentifiedText';
+import {InfoDialog} from './InfoDialog';
+import {DeidentificationConfigForm} from './DeidentificationConfigForm';
+import AnnotationView from './AnnotationView';
-const config = new Config()
+const config = new Config();
const apiConfiguration = new Configuration({basePath: config.serverApiUrl()});
const deidentifiedNotesApi = new DeidentifiedNoteApi(apiConfiguration);
const toolApi = new ToolApi(apiConfiguration);
+const defaultText =
+ 'On 12/26/2020, Ms. Chloe Price met with Dr. Prescott in Seattle.';
+
const styles = (theme) => {
return {
root: {
- backgroundColor: "#282c34",
- minHeight: "100vh",
- justifyContent: "center",
- color: "white",
- overflow: "auto",
- padding: "20px",
- paddingBottom: "50px"
+ backgroundColor: '#282c34',
+ minHeight: '100vh',
+ justifyContent: 'center',
+ color: 'white',
+ overflow: 'auto',
+ padding: '20px',
+ paddingBottom: '50px',
},
deidButton: {
- backgroundColor: "#ADD8E6"
- }
+ backgroundColor: '#ADD8E6',
+ },
};
-}
+};
class App extends React.Component {
+ static propTypes = {
+ location: PropTypes.object,
+ history: PropTypes.object,
+ };
+
constructor(props) {
super(props);
// Try loading state from URL
- const { location } = props;
+ const {location} = props;
const queryInUrl = location.pathname.slice(1);
let deidentifyRequest;
let showInfo;
@@ -56,16 +66,17 @@ class App extends React.Component {
deidentificationSteps: [{
key: 0,
confidenceThreshold: 20,
- maskingCharConfig: {maskingChar: "*"},
- annotationTypes: ["text_person_name", "text_physical_address", "text_date"]
+ maskingCharConfig: {maskingChar: '*'},
+ annotationTypes: [
+ 'text_person_name', 'text_physical_address', 'text_date'],
}],
note: {
- text: "On 12/26/2020, Ms. Chloe Price met with Dr. Prescott in Seattle.",
- noteType: "0000", // FIXME: figure out whether and how to get this
- identifier: "0000",
- patientId: "0000"
+ text: defaultText,
+ noteType: '0000', // FIXME: figure out whether and how to get this
+ identifier: '0000',
+ patientId: '0000',
},
- keyMax: 0
+ keyMax: 0,
};
showInfo = true;
}
@@ -74,58 +85,62 @@ class App extends React.Component {
deidentifiedNoteText: deidentificationStates.EMPTY,
deidentifiedAnnotations: deidentificationStates.EMPTY,
deidentifyRequest: deidentifyRequest,
- showInfo: showInfo
+ showInfo: showInfo,
};
this.handleTextAreaChange = this.handleTextAreaChange.bind(this);
}
updateUrl = () => {
- const queryInUrl = "/" + encodeString(JSON.stringify(this.state.deidentifyRequest));
+ const queryInUrl = '/' +
+ encodeString(JSON.stringify(this.state.deidentifyRequest));
this.props.history.push(queryInUrl);
}
deidentifyNote = () => {
// Mark de-identified text as loading
- this.setState({deidentifiedNoteText: deidentificationStates.LOADING})
+ this.setState({deidentifiedNoteText: deidentificationStates.LOADING});
// Build de-identification request
- let deidentifyRequest = new DeidentifyRequestFromJSON(this.state.deidentifyRequest);
+ const deidentifyRequest =
+ new DeidentifyRequestFromJSON(this.state.deidentifyRequest);
// Make de-identification request
- deidentifiedNotesApi.createDeidentifiedNotes({deidentifyRequest: deidentifyRequest})
+ deidentifiedNotesApi.createDeidentifiedNotes(
+ {deidentifyRequest: deidentifyRequest})
.then((deidentifyResponse) => {
this.setState({
deidentifiedNoteText: deidentifyResponse.deidentifiedNote.text,
- deidentifiedAnnotations: deidentifyResponse.deidentifiedAnnotations
+ deidentifiedAnnotations: deidentifyResponse.deidentifiedAnnotations,
});
})
.catch(() => {
this.setState({
deidentifiedNoteText: deidentificationStates.ERROR,
- deidentifiedAnnotations: deidentificationStates.ERROR
+ deidentifiedAnnotations: deidentificationStates.ERROR,
});
});
}
replaceDeidentificationStep = (index, newStep) => {
- let deidentificationSteps = [...this.state.deidentifyRequest.deidentificationSteps];
+ const deidentificationSteps =
+ [...this.state.deidentifyRequest.deidentificationSteps];
deidentificationSteps[index] = newStep;
this.setState(
{
deidentifyRequest: {
...this.state.deidentifyRequest,
- deidentificationSteps: deidentificationSteps
- }
+ deidentificationSteps: deidentificationSteps,
+ },
},
- () => this.updateUrl()
+ () => this.updateUrl(),
);
}
-
+
updateDeidentificationStep = (index, newSettings) => {
const newStep = {
...this.state.deidentifyRequest.deidentificationSteps[index],
- ...newSettings
+ ...newSettings,
};
this.replaceDeidentificationStep(index, newStep);
}
@@ -137,21 +152,23 @@ class App extends React.Component {
...this.state.deidentifyRequest,
note: {
...this.state.deidentifyRequest.note,
- text: event.target.value
- }
- }
+ text: event.target.value,
+ },
+ },
},
- () => this.updateUrl()
+ () => this.updateUrl(),
);
}
addDeidStep = (event) => {
- let deidentificationSteps = [...this.state.deidentifyRequest.deidentificationSteps];
+ const deidentificationSteps =
+ [...this.state.deidentifyRequest.deidentificationSteps];
const newDeidStep = {
confidenceThreshold: 20,
- maskingCharConfig: {maskingChar: "*"},
- annotationTypes: ["text_person_name", "text_physical_address", "text_date"],
- key: this.state.deidentifyRequest.keyMax+1
+ maskingCharConfig: {maskingChar: '*'},
+ annotationTypes: [
+ 'text_person_name', 'text_physical_address', 'text_date'],
+ key: this.state.deidentifyRequest.keyMax+1,
};
deidentificationSteps.push(newDeidStep);
this.setState(
@@ -159,42 +176,55 @@ class App extends React.Component {
deidentifyRequest: {
...this.state.deidentifyRequest,
deidentificationSteps: deidentificationSteps,
- keyMax: this.state.deidentifyRequest.keyMax + 1
- }
+ keyMax: this.state.deidentifyRequest.keyMax + 1,
+ },
},
- () => this.updateUrl()
+ () => this.updateUrl(),
);
}
redoDeidentificationStep = (index, oldKey, newKey, newValue) => {
// Delete a key from a deid step, and add a new key, value pair to it
- let {[oldKey]: omitted, ...newDeidStep} = this.state.deidentifyRequest.deidentificationSteps[index];
+ /* eslint-disable no-unused-vars */
+ const {[oldKey]: omitted, ...newDeidStep} =
+ this.state.deidentifyRequest.deidentificationSteps[index];
+ /* eslint-enable no-unused-vars */
newDeidStep[newKey] = newValue;
-
+
this.replaceDeidentificationStep(index, newDeidStep);
}
deleteDeidentificationStep = (index) => {
- let deidentificationSteps = [...this.state.deidentifyRequest.deidentificationSteps];
+ const deidentificationSteps =
+ [...this.state.deidentifyRequest.deidentificationSteps];
deidentificationSteps.splice(index, 1);
this.setState(
{
deidentifyRequest: {
...this.state.deidentifyRequest,
- deidentificationSteps: deidentificationSteps
- }
+ deidentificationSteps: deidentificationSteps,
+ },
},
- () => this.updateUrl()
+ () => this.updateUrl(),
);
}
render() {
- const { classes } = this.props;
+ const {classes} = this.props;
- const leftColumn =
+ const leftColumn =
- Input Note
+
+ Input Note
+
@@ -210,16 +240,20 @@ class App extends React.Component {
-
+
- Deidentification Steps
+
+ Deidentification Steps
+
{
- this.state.deidentifyRequest.deidentificationSteps.map((deidStep, index) =>
-
+ this.state.deidentifyRequest.deidentificationSteps.map(
+ (deidStep, index) =>
-
+ ,
)
}
-
+ ;
- const rightColumn =
+ const rightColumn =
- De-identified Note
+
+ De-identified Note
+
@@ -246,37 +290,49 @@ class App extends React.Component {
- Annotations
+
+ Annotations
+
-
+ ;
return (
-
-
-
- NLP Sandbox PHI Deidentifier
- {this.setState({showInfo: true})}}>
-
-
-
-
- {leftColumn}
-
- {rightColumn}
-
-
-
{this.setState({showInfo: false})}}
- toolApi={toolApi}
- />
-
+
+
+
+
+ NLP Sandbox PHI Deidentifier
+
+ {
+ this.setState({showInfo: true});
+ }}>
+
+
+
+
+ {leftColumn}
+
+ {rightColumn}
+
+
+
{
+ this.setState({showInfo: false});
+ }}
+ toolApi={toolApi}
+ />
+
);
}
}
+App.propTypes = {
+ classes: PropTypes.object,
+};
+
export default withStyles(styles)(App);
diff --git a/client/src/components/DeidentificationConfigForm.js b/client/src/components/DeidentificationConfigForm.js
index 97b3e80..88ac51c 100644
--- a/client/src/components/DeidentificationConfigForm.js
+++ b/client/src/components/DeidentificationConfigForm.js
@@ -1,28 +1,39 @@
import React from 'react';
-import { DeidentificationStepAnnotationTypesEnum } from '../models';
-import { Collapse, Paper, Table, TableRow, TableCell, AppBar, Toolbar,
+import {DeidentificationStepAnnotationTypesEnum} from '../models';
+import {Collapse, Paper, Table, TableRow, TableCell, AppBar, Toolbar,
Typography, IconButton, TextField, Select, MenuItem} from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import RemoveCircleOutlineIcon from '@material-ui/icons/RemoveCircleOutline';
+import PropTypes from 'prop-types';
const DEIDENTIFICATION_STRATEGIES = {
- "maskingCharConfig": "Masking Character",
- "annotationTypeMaskConfig": "Annotation Type Mask",
- "redactConfig": "Redaction"
-}
+ 'maskingCharConfig': 'Masking Character',
+ 'annotationTypeMaskConfig': 'Annotation Type Mask',
+ 'redactConfig': 'Redaction',
+};
const ANNOTATION_TYPE_NAMES = {
- "text_date": "Date",
- "text_person_name": "Person Name",
- "text_physical_address": "Physical Address"
-}
+ 'text_date': 'Date',
+ 'text_person_name': 'Person Name',
+ 'text_physical_address': 'Physical Address',
+};
export class DeidentificationConfigForm extends React.Component {
+ static propTypes = {
+ updateDeidStep: PropTypes.func.isRequired,
+ redoDeidStep: PropTypes.func.isRequired,
+ deleteDeidStep: PropTypes.func.isRequired,
+ annotationTypes: PropTypes.array.isRequired,
+ confidenceThreshold: PropTypes.number.isRequired,
+ index: PropTypes.number.isRequired,
+ maskingCharConfig: PropTypes.object,
+ };
+
constructor(props) {
super(props);
this.state = {
- expand: false
- }
+ expand: false,
+ };
}
updateDeidStep = (newSettings) => {
@@ -30,11 +41,12 @@ export class DeidentificationConfigForm extends React.Component {
}
getStrategy() {
- // Return the current deidentification strategy for this deidentification step
+ // Return the current deidentification strategy for this deidentification
+ // step
let strategy;
- const deidStrategies = Object.keys(DEIDENTIFICATION_STRATEGIES)
+ const deidStrategies = Object.keys(DEIDENTIFICATION_STRATEGIES);
for (let i = 0; i < deidStrategies.length; i++) {
- strategy = deidStrategies[i]
+ strategy = deidStrategies[i];
if (strategy in this.props) {
return strategy;
}
@@ -44,10 +56,14 @@ export class DeidentificationConfigForm extends React.Component {
handleStrategyChange = (event) => {
const newStrategyName = event.target.value;
const oldStrategyName = this.getStrategy();
- if (newStrategyName === "maskingCharConfig") {
- this.props.redoDeidStep(this.props.index, oldStrategyName, newStrategyName, {maskingChar: "*"});
+ if (newStrategyName === 'maskingCharConfig') {
+ this.props.redoDeidStep(
+ this.props.index, oldStrategyName, newStrategyName, {maskingChar: '*'},
+ );
} else {
- this.props.redoDeidStep(this.props.index, oldStrategyName, newStrategyName, {})
+ this.props.redoDeidStep(
+ this.props.index, oldStrategyName, newStrategyName, {},
+ );
}
}
@@ -55,61 +71,73 @@ export class DeidentificationConfigForm extends React.Component {
const maskingChar = event.target.value;
if (maskingChar) {
this.updateDeidStep({
- maskingCharConfig: { maskingChar: maskingChar }
+ maskingCharConfig: {maskingChar: maskingChar},
});
} else {
this.updateDeidStep({
- maskingCharConfig: {}
+ maskingCharConfig: {},
});
}
}
handleConfidenceThresholdChange = (event) => {
this.updateDeidStep({
- confidenceThreshold: parseFloat(event.target.value)
+ confidenceThreshold: parseFloat(event.target.value),
});
}
handleAnnotationTypeDelete = (event, index) => {
- const annotationTypes = this.props.annotationTypes
- const newAnnotationTypes = annotationTypes.slice(0, index).concat(annotationTypes.slice(index+1));
+ const annotationTypes = this.props.annotationTypes;
+ const newAnnotationTypes =
+ annotationTypes.slice(0, index).concat(annotationTypes.slice(index+1));
this.updateDeidStep({
- annotationTypes: newAnnotationTypes
+ annotationTypes: newAnnotationTypes,
});
}
handleAnnotationTypeAdd = (event) => {
const annotationType = event.target.value;
this.updateDeidStep({
- annotationTypes: this.props.annotationTypes.concat(annotationType)
+ annotationTypes: this.props.annotationTypes.concat(annotationType),
});
}
handleDelete = () => {
this.setState(
- { expand: false }, () => {
+ {expand: false}, () => {
setTimeout(
- () => {this.props.deleteDeidStep(this.props.index);},
- 250
- )
- }
+ () => {
+ this.props.deleteDeidStep(this.props.index);
+ },
+ 250,
+ );
+ },
);
}
componentDidMount = () => {
- this.setState({ expand: true });
+ this.setState({expand: true});
}
render = () => {
- const allAnnotationTypes = Object.values(DeidentificationStepAnnotationTypesEnum)
- const borderRadius = 10
+ const allAnnotationTypes =
+ Object.values(DeidentificationStepAnnotationTypesEnum);
+ const borderRadius = 10;
return (
-
-
+
+
- De-identification Step #{this.props.index + 1}
-
+
+ De-identification Step #{this.props.index + 1}
+
+
+
+
@@ -118,20 +146,25 @@ export class DeidentificationConfigForm extends React.Component {
Obfuscation method
-