Skip to content

Commit af84863

Browse files
Fb chip reader dev (#276)
* New dev for animal ID chip reader * Merge develop changes into feature branch (#275) * Item 7490: upgrade log4j (#261) * Added acquisition and disposition categories to demographics queries (#272) * Enable Charts/Reports Animal History tabbed reports (#274) Co-authored-by: Ankur Juneja <ankurj@labkey.com> * New dev - chip reader updates * New dev - more chip reader updates * New dev - more chip reader updates * More files * New dev - lint fixes * New dev - updated snapshot * New dev - automated testing * bug fix Co-authored-by: Ankur Juneja <ankurj@labkey.com>
1 parent 296fa36 commit af84863

31 files changed

Lines changed: 1695 additions & 16 deletions

.gitignore

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ snprc_ehr/resources/views/HelloApp*.*
1616
snprc_ehr/resources/views/helloWorld*.*
1717
snprc_ehr/resources/views/NewAnimalPage*.*
1818
snprc_ehr/resources/views/BirthRecordReport*.*
19+
snprc_ehr/resources/views/ChipReader*.*
1920
snprc_ehr/cmd-here.exe
2021
snprc_ehr/resources/referenceStudy/*.xlsx
2122
snprc_ehr/resources/web/snprc_ehr/snprcReports.js.gz
2223
snprc_ehr/resources/web/snprc_ehr/gen/*
2324
snprc_mobile
24-
/snprc_ehr.2020*.zip
25-
/snprc_ehr.2020*.7z
26-
/snprc_ehr/.editorconfig
27-
/snprc_ehr/cmd-here.exe
25+
snprc_ehr.2020*.zip
26+
snprc_ehr.2020*.7z
27+
snprc_ehr/.editorconfig
28+
snprc_ehr/cmd-here.exe
29+
snprc_ehr/resources/referenceStudy/LoadTaqmanData
30+
snprc_ehr/resources/referenceStudy/datasetImport/LoadTaqmanData

snprc_ehr/.babelrc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"presets":[
3-
"@babel/preset-env",
4-
"@babel/preset-react"
3+
"@babel/preset-env",
4+
"@babel/preset-react"
55
],
66
"plugins": [
77
"transform-class-properties",
8-
"@babel/plugin-proposal-object-rest-spread"
8+
"@babel/plugin-proposal-object-rest-spread",
9+
"@babel/plugin-transform-runtime"
910
]
1011
}

snprc_ehr/.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"error",
5050
"as-needed"
5151
],
52+
"lines-between-class-members": ["error", "never"],
5253
"react/jsx-filename-extension": 0,
5354
"react/sort-comp": 0,
5455
"global-require": 0,

snprc_ehr/package-lock.json

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

snprc_ehr/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"@babel/core": "^7.10.2",
6868
"@babel/plugin-proposal-class-properties": "^7.10.1",
6969
"@babel/plugin-proposal-object-rest-spread": "^7.10.1",
70+
"@babel/plugin-transform-runtime": "^7.11.0",
7071
"@babel/polyfill": "7.4.4",
7172
"@babel/preset-env": "7.4.5",
7273
"@babel/preset-react": "7.0.0",

snprc_ehr/resources/queries/study/idHistory.query.xml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22
<metadata>
33
<tables xmlns="http://labkey.org/data/xml">
44
<table tableName="idHistory" tableDbType="TABLE" useColumnOrder="true">
5-
<tableUrl />
6-
<insertUrl />
7-
<importUrl />
8-
<updateUrl />
9-
<deleteUrl />
105
<columns>
116
<column columnName="Id">
127
<columnTitle>TxBiomed ID</columnTitle>
@@ -57,4 +52,4 @@
5752
</table>
5853
</tables>
5954
</metadata>
60-
</query>
55+
</query>

snprc_ehr/resources/views/begin.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
},
138138
items: [
139139
{ name: 'Compare Lists of Animals', url: '<%=contextPath%>/ehr' + ctx['EHRStudyContainer'] + '/utilities.view?' },
140+
{name: 'Chip Reader',url: '<%=contextPath%>/snprc_ehr' + ctx['EHRStudyContainer'] + '/ChipReader.view?'}
140141
//{name: 'Drug Formulary', url: '<%=contextPath%>/query' + ctx['EHRStudyContainer'] + '/executeQuery.view?schemaName=ehr_lookups&query.queryName=drug_defaults'},
141142
//{name: 'Procedure List', url: '<%=contextPath%>/query' + ctx['EHRStudyContainer'] + '/executeQuery.view?schemaName=ehr_lookups&query.queryName=procedures&query.viewName=Active Procedures'},
142143
//{name: 'Search Center SNOMED Codes', url: '<%=contextPath%>/query' + ctx['EHRStudyContainer'] + '/executeQuery.view?schemaName=ehr_lookups&query.queryName=snomed'},
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/* eslint-disable no-alert */
2+
3+
import React from 'react'
4+
import { LoadingSpinner } from '@labkey/components'
5+
import './styles/chipReader.scss'
6+
import ChipReaderState from './constants/chipReaderState'
7+
import constants from './constants/index'
8+
import InfoPanel from '../Shared/components/InfoPanel'
9+
import ChipDataPanel from './components/ChipDataPanel'
10+
import SummaryGridPanel from './components/SummaryGridPanel'
11+
import fetchAnimalId from './api/fetchAnimalId'
12+
import { close } from './services/serialService'
13+
14+
export default class ChipReader extends React.Component {
15+
state = new ChipReaderState();
16+
notSupportedMessage = constants.notSupportedMessage
17+
debug = constants.debug;
18+
isSerialSupported = ('serial' in navigator) || this.props.debug
19+
previousChipId = undefined
20+
componentDidMount() {
21+
window.addEventListener('beforeunload', this.beforeunload.bind(this))
22+
23+
this.setState(prevState => (
24+
{
25+
...prevState,
26+
serialOptions: constants.defaultSerialOptions,
27+
isLoading: false
28+
}
29+
))
30+
}
31+
componentWillUnmount() {
32+
window.removeEventListener('beforeunload', this.beforeunload.bind(this))
33+
}
34+
beforeunload(e) {
35+
if (this.state.connection) {
36+
close(this.state.connection)
37+
e.preventDefault()
38+
e.returnValue = true
39+
}
40+
}
41+
handleSetConnection = connection => {
42+
this.setState(prevState => (
43+
{
44+
...prevState,
45+
connection
46+
}
47+
))
48+
}
49+
handleDataChange = async value => {
50+
const data = value || { chipId: undefined, animalId: undefined, temperature: undefined }
51+
52+
if (!data.chipId || data.chipId === this.previousChipId) return
53+
54+
// console.log(`chipId = ${data.chipId}`)
55+
this.previousChipId = data.chipId
56+
57+
if (data.chipId && !data.animalId) {
58+
data.animalId = await fetchAnimalId(data.chipId).catch(error => {
59+
this.setState(prevState => (
60+
{
61+
...prevState,
62+
errorMessage: error.message
63+
}
64+
))
65+
})
66+
}
67+
68+
this.setState(prevState => ({
69+
...prevState,
70+
chipData: data,
71+
...(data.animalId && { errorMessage: undefined }), // clear error message
72+
...(data.animalId && {
73+
summaryData: [
74+
...prevState.summaryData,
75+
{ ...data }
76+
]
77+
})
78+
}))
79+
}
80+
handleErrorMessage = error => {
81+
this.setState(prevState => (
82+
{
83+
...prevState,
84+
errorMessage: error
85+
}
86+
))
87+
}
88+
render() {
89+
// allow debug mode to be triggered for running test suite
90+
this.debug = this.props.debug !== undefined ? this.props.debug : constants.debug
91+
92+
const { isLoading } = this.state
93+
94+
if (isLoading) {
95+
return (
96+
<LoadingSpinner msg="Loading app..." />
97+
)
98+
}
99+
100+
return (
101+
<div>
102+
{ this.isSerialSupported
103+
&& (
104+
<div className="split-panel">
105+
<div className="parent-panel">
106+
<div className="panel-heading">
107+
<p>Current Animal</p>
108+
</div>
109+
<div className="wizard-panel">
110+
<ChipDataPanel
111+
chipData={ this.state.chipData }
112+
connection={ this.state.connection }
113+
errorMessage={ this.state.errorMessage }
114+
handleDataChange={ this.handleDataChange }
115+
handleErrorMessage={ this.handleErrorMessage }
116+
handleSetConnection={ this.handleSetConnection }
117+
serialOptions={ this.state.serialOptions }
118+
/>
119+
</div>
120+
121+
</div>
122+
123+
<div className="right-panel">
124+
<div>
125+
<div className="panel-heading">
126+
<p>Animals Seen</p>
127+
</div>
128+
<div className="wizard-right">
129+
<SummaryGridPanel
130+
summaryData={ this.state.summaryData }
131+
/>
132+
</div>
133+
</div>
134+
</div>
135+
</div>
136+
) }
137+
<InfoPanel
138+
errorMessages={ (!this.isSerialSupported
139+
&& [{ propTest: true, colName: this.notSupportedMessage }])
140+
|| (this.state.errorMessage && [{ propTest: true, colName: this.state.errorMessage }]) }
141+
/>
142+
</div>
143+
)
144+
}
145+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { executeSql } from '../../Shared/api/api'
2+
import moment from 'moment'
3+
4+
const parse = rows => {
5+
return rows.map(({ data }) => {
6+
return { value: data.Id.value, url: data.Id.url, location: data.location.value, time: moment() }
7+
})
8+
}
9+
10+
const fetchAnimalId = chipId => {
11+
const sql = `SELECT ih.Id AS Id, ih.Id.curLocation.location as location
12+
FROM study.idHistory as ih
13+
WHERE ih.value = '${chipId}'`
14+
15+
return new Promise((resolve, reject) => {
16+
17+
executeSql({
18+
schemaName: 'study',
19+
sql
20+
}).then(({ rows }) => {
21+
const parseRows = parse(rows)
22+
if (parseRows.length !== 1)
23+
throw new Error ("Animal not found")
24+
resolve(parseRows[0])
25+
}).catch(error => {
26+
reject(error)
27+
})
28+
})
29+
}
30+
export default fetchAnimalId
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
4+
import ChipReader from './ChipReader'
5+
6+
// Need to wait for container element to be available in labkey wrapper before render
7+
window.addEventListener('DOMContentLoaded', () => {
8+
ReactDOM.render(<ChipReader />, document.getElementById('app'))
9+
})

0 commit comments

Comments
 (0)