Skip to content

Commit 55bc308

Browse files
committed
Added PDF parsing for earnings record upload
1 parent 6cd6021 commit 55bc308

30 files changed

+531
-11767
lines changed

createStore.js

-15
This file was deleted.

package.json

+5-6
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
"@emotion/core": "^10.0.10",
99
"@emotion/styled": "^10.0.10",
1010
"@observablehq/runtime": "^4.0.1",
11+
"@reach/router": "^1.2.1",
1112
"@types/react-helmet": "^5.0.8",
1213
"babel-preset-gatsby": "^0.1.11",
13-
"core-js": "^2.6.5",
1414
"fast-xml-parser": "^3.12.16",
1515
"gatsby": "^2.4.2",
1616
"gatsby-cli": "^2.5.13",
@@ -24,21 +24,20 @@
2424
"gatsby-plugin-typescript": "^2.0.11",
2525
"gatsby-source-filesystem": "^2.0.28",
2626
"gatsby-transformer-sharp": "^2.1.17",
27-
"how-to-embed-a-notebook-in-a-react-app": "https://api.observablehq.com/@jashkenas/how-to-embed-a-notebook-in-a-react-app.tgz?key=188f50f6a5f19b89",
27+
"pdfjs-dist": "^2.1.266",
2828
"prop-types": "^15.7.2",
2929
"react": "^16.8.5",
3030
"react-dom": "^16.8.5",
3131
"react-helmet": "^5.2.0",
32-
"tesseract.js": "^1.0.14",
32+
"tesseract.js": "^2.0.0-alpha.11",
3333
"typescript": "^3.3.4000",
34-
"windfall-awareness-notebook-prototype": "https://api.observablehq.com/@nvanwittenbe/windfall-awareness-notebook-prototype.tgz"
34+
"windfall-awareness-notebook-prototype": "https://api.observablehq.com/@thadk/windfall-awareness-notebook-prototype.tgz?v=1"
3535
},
3636
"devDependencies": {
3737
"@types/react": "^16.8.8",
3838
"@types/react-dom": "^16.8.3",
3939
"babel-plugin-emotion": "^10.0.9",
40-
"prettier": "^1.16.4",
41-
"react-redux": "^7.0.2"
40+
"prettier": "^1.16.4"
4241
},
4342
"keywords": [
4443
"gatsby"

readme.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@ Join the Slack channel #windfall-elimination for more information.
88
## Launching Gatsby Site
99
Install the gatsby-cli with `npm install -g gatsby-cli`
1010

11-
Run `npm install && gatsby develop` to launch the project.
12-
11+
Run `npm install && gatsby develop` to launch the project.

redux-store.js

-13
This file was deleted.

src/components/button-link-red.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { ButtonLink } from "../components";
33
import { colors } from "../constants";
44

55
export const ButtonLinkRed = styled(ButtonLink)`
6-
background-color: ${colors.white};
7-
color: ${colors.black};
8-
border: 2px solid ${colors.lime};
9-
&:hover {
10-
border-color: ${colors.lime};
11-
background-color: ${colors.lime};
12-
color: ${colors.white}
13-
}
6+
background-color: ${colors.white};
7+
color: ${colors.black};
8+
border: 2px solid ${colors.lime};
9+
&:hover {
10+
border-color: ${colors.lime};
11+
background-color: ${colors.lime};
12+
color: ${colors.white};
13+
}
1414
`;

src/components/button-link.tsx

+16-17
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@ import { Link } from "gatsby";
33
import { spacing, colors, fontSizes, radii } from "../constants";
44

55
export const ButtonLink = styled(Link)`
6-
padding: ${spacing[1]} ${spacing[2]};
7-
margin: ${spacing[2]} ${spacing[2]};
8-
background-color: ${props =>
9-
props.disabled ? colors.gray : colors.darkGreen};
10-
border-radius: ${radii[2]};
11-
font-size: ${fontSizes[1]};
12-
color: ${colors.white};
13-
text-decoration: none;
14-
display: inline-block;
15-
border: 2px solid ${colors.darkGreen};
16-
&:hover {
17-
background-color: ${colors.white};
18-
color: ${colors.darkGreen};
19-
cursor: pointer;
20-
}
21-
pointer-events: ${props => props.disabled && "none"};
22-
6+
padding: ${spacing[1]} ${spacing[2]};
7+
margin: ${spacing[2]} ${spacing[2]};
8+
background-color: ${props =>
9+
props.disabled ? colors.gray : colors.darkGreen};
10+
border-radius: ${radii[2]};
11+
font-size: ${fontSizes[1]};
12+
color: ${colors.white};
13+
text-decoration: none;
14+
display: inline-block;
15+
border: 2px solid ${colors.darkGreen};
16+
&:hover {
17+
background-color: ${colors.white};
18+
color: ${colors.darkGreen};
19+
cursor: pointer;
20+
}
21+
pointer-events: ${props => props.disabled && "none"};
2322
`;

src/components/card.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import styled from "@emotion/styled";
22
import { colors, radii, spacing } from "../constants";
33

44
export const Card = styled("div")`
5-
border: 1px solid ${colors.darkGreen};
6-
border-radius: ${radii[0]};
7-
padding: ${spacing[1]};
8-
margin: ${spacing[1]};
5+
border: 1px solid ${colors.darkGreen};
6+
border-radius: ${radii[0]};
7+
padding: ${spacing[1]};
8+
margin: ${spacing[1]};
99
`;

src/components/file-upload.jsx

+89-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import styled from "@emotion/styled";
3-
import fastXml from 'fast-xml-parser';
3+
import fastXml from "fast-xml-parser";
4+
import pdfJS from "pdfjs-dist";
45
import { spacing, colors, fontSizes, radii } from "../constants";
56
import { ObservableCell, SessionStore } from "../components";
67

@@ -98,7 +99,7 @@ export class GenerateTable extends React.Component {
9899
)
99100

100101
})
101-
102+
102103
} else {
103104
header = <tr></tr>;
104105
tableRows = <tr></tr>;
@@ -120,8 +121,11 @@ export default class FileUpload extends React.Component {
120121
constructor(props, context) {
121122
super(props, context);
122123

124+
pdfJS.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/[email protected]/build/pdf.worker.js';
125+
123126
this.handleUpload = this.handleUpload.bind(this);
124-
this.handleLoadTable = this.handleLoadTable.bind(this);
127+
this.handleXMLFile = this.handleXMLFile.bind(this);
128+
this.handlePDFFile = this.handlePDFFile.bind(this);
125129
this.handleInputEarnings = this.handleInputEarnings.bind(this);
126130
this.handleManualEarnings = this.handleManualEarnings.bind(this);
127131
this.handleSave = this.handleSave.bind(this);
@@ -159,7 +163,7 @@ export default class FileUpload extends React.Component {
159163

160164
if ((this.state.userBirthDate) && (this.state.userRetireDate) && (!this.state.manualTable.length)) {
161165
var tempTable = []
162-
166+
163167
for (var i = this.state.userBirthDate; i <= this.state.userRetireDate; i++) {
164168
var record = {}
165169
record['year'] = i
@@ -181,14 +185,14 @@ export default class FileUpload extends React.Component {
181185
earningsRecord: earningsValue
182186
})
183187
}
184-
188+
185189
if (SessionStore.get('tableArray')) {
186190
var tableArray = JSON.parse(SessionStore.get('tableArray'))
187191
this.setState({
188-
manualTable: tableArray
192+
manualTable: tableArray
189193
})
190194
}
191-
195+
192196
}
193197

194198

@@ -231,7 +235,9 @@ export default class FileUpload extends React.Component {
231235
if (element['@_startYear'] === modifiedyear) {
232236
element['osss:FicaEarnings'] = input.target.value
233237
return true
234-
}
238+
} else {
239+
return false
240+
}
235241
})
236242

237243
if (findValue) {
@@ -248,7 +254,7 @@ export default class FileUpload extends React.Component {
248254
}
249255

250256
//Parse XML file
251-
handleLoadTable(reader) {
257+
handleXMLFile(reader) {
252258
if (fastXml.validate(reader.target.result) === true) {
253259
var parsedText = fastXml.parse(reader.target.result, {ignoreAttributes: false})
254260
var earningsJSON = JSON.stringify(parsedText)
@@ -259,24 +265,83 @@ export default class FileUpload extends React.Component {
259265
}
260266
}
261267

268+
//Parse PDF file
269+
handlePDFFile(reader) {
270+
//Returns first page of document
271+
var combinedValues = []
272+
pdfJS.getDocument(reader.target.result).promise
273+
.then(
274+
ssaDoc => ssaDoc.getPage(3)).then(earningsPage => earningsPage.getTextContent())
275+
.then( (doc) => {
276+
doc.items.forEach((item) => {
277+
278+
var filter = Number(item.str.replace(",", "").replace(" ",""))
279+
if (!(Number.isNaN(filter))) {
280+
combinedValues.push(filter)
281+
}
282+
})
283+
})
284+
.then(() => {
285+
286+
var tempRecord = this.state.defaultRecord
287+
do {
288+
var newvalue = combinedValues.shift()
289+
if (newvalue > 1900) {
290+
var currentRecord = tempRecord['osss:OnlineSocialSecurityStatementData']['osss:EarningsRecord']['osss:Earnings']
291+
var newrecord = {
292+
'@_startYear': newvalue,
293+
'@_endYear': newvalue,
294+
'osss:FicaEarnings': combinedValues.shift(),
295+
'osss:MedicafreEarnings': combinedValues.shift()
296+
}
297+
298+
currentRecord.push(newrecord)
299+
}
300+
} while (combinedValues.length > 0)})
301+
.then(() => {
302+
var earningsJSON = JSON.stringify(this.state.defaultRecord)
303+
SessionStore.push('earnings', earningsJSON)
304+
this.setState({
305+
earningsRecord: this.state.defaultRecord
306+
})
307+
})
308+
}
309+
262310
handleUpload(formResponse) {
263311
this.setState({
264312
displayTable: true
265313
});
266314
formResponse.preventDefault();
267315
const file = this.fileInput.current.files[0]
268-
const name = this.fileInput.current.files[0].name
269-
const formData = new FormData();
270-
formData.append(name, file)
271-
316+
var name = this.fileInput.current.files[0].name
317+
name = name.split('.')
318+
const extension = name[name.length - 1]
272319
var reader = new FileReader()
273-
reader.readAsText(file);
274320

275-
reader.onload = (reader) => this.handleLoadTable(reader)
321+
switch (extension) {
322+
case 'xml':
323+
reader.onload = (reader) => this.handleXMLFile(reader)
324+
reader.readAsText(file);
325+
break;
326+
327+
case 'pdf':
328+
reader.onload = (reader) => this.handlePDFFile(reader)
329+
reader.readAsArrayBuffer(file)
330+
break;
331+
332+
default:
333+
alert("I'm sorry, that file was not recognized.")
334+
break;
335+
}
336+
337+
338+
339+
276340
}
277341

278342
//Stores users input for manually entered table to allow for persistence across page changes
279343
handleManualEarnings(input) {
344+
// eslint-disable-next-line
280345
const [type, year, key] = input.target.id.split('_')
281346

282347
var tempManualTable = this.state.manualTable
@@ -292,17 +357,17 @@ export default class FileUpload extends React.Component {
292357

293358
//Saves manually entered record to this.state.earningsRecord object, becomes noticable to Observable API
294359
handleSave() {
295-
var tempRecord = this.state.earningsRecord ?
360+
var tempRecord = this.state.earningsRecord ?
296361
this.state.earningsRecord['osss:OnlineSocialSecurityStatementData']['osss:EarningsRecord']['osss:Earnings'].length === this.state.manualTable.length
297362
? this.state.earningsRecord : this.state.defaultRecord
298363
:
299364
this.state.defaultRecord
300365

301-
this.state.manualTable.map((record, i) => {
366+
this.state.manualTable.forEach((record, i) => {
302367
var currentRecord = tempRecord['osss:OnlineSocialSecurityStatementData']['osss:EarningsRecord']['osss:Earnings']
303368
var newrecord = {
304369
'@_startYear': record['year'],
305-
'@_endYear': record['year'],
370+
'@_endYear': record['year'],
306371
'osss:FicaEarnings': record['value']
307372
}
308373

@@ -311,7 +376,7 @@ export default class FileUpload extends React.Component {
311376
} else {
312377
currentRecord[i] = newrecord
313378
}
314-
379+
315380
})
316381

317382
var arrayJSON = JSON.stringify(this.state.manualTable)
@@ -340,10 +405,10 @@ export default class FileUpload extends React.Component {
340405
<UploadLabel htmlFor="inputfile" className="btn">{this.state.buttonText}</UploadLabel>
341406
<UploadInput type={this.state.buttonType} id='inputfile' ref={this.fileInput} onChange={this.state.buttonFunction}></UploadInput>
342407
</UploadButton>
343-
<GenerateTable
344-
parsedXml={this.state.earningsRecord}
345-
handleInputEarnings={this.handleInputEarnings}
346-
manual={this.props.manual}
408+
<GenerateTable
409+
parsedXml={this.state.earningsRecord}
410+
handleInputEarnings={this.handleInputEarnings}
411+
manual={this.props.manual}
347412
manualTable={this.state.manualTable}
348413
handleManualEarnings={this.handleManualEarnings}
349414
handleSave={this.handleSave}
@@ -356,4 +421,4 @@ export default class FileUpload extends React.Component {
356421
</div>
357422
)
358423
}
359-
}
424+
}

0 commit comments

Comments
 (0)