Skip to content

Commit 7242b52

Browse files
Added support for vsv slide images
1 parent aa24728 commit 7242b52

File tree

17 files changed

+667
-3
lines changed

17 files changed

+667
-3
lines changed

client/src/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class App extends Component {
6060
<Nav.Link href="/molecularqcs">Molecular QCs</Nav.Link>
6161
<Nav.Link href="/iscans">iScans</Nav.Link>
6262
<Nav.Link href="/proteins">Proteins</Nav.Link>
63+
<Nav.Link href="/images">Images</Nav.Link>
6364
</Nav>
6465
) : (
6566
<Nav className="mr-auto">

client/src/Routes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ShipmentReceipt from "./containers/ShipmentReceipts";
55
import MolecularQC from "./containers/MolecularQCs";
66
import IScan from "./containers/IScans";
77
import Protein from "./containers/Proteins";
8+
import Images from "./containers/Images";
89
import NotFound from "./containers/NotFound";
910

1011
export default ({ childProps }) =>
@@ -14,5 +15,6 @@ export default ({ childProps }) =>
1415
{ childProps.isDataManager && <Route path="/molecularqcs/:id?" component={MolecularQC} /> }
1516
{ childProps.isDataManager && <Route path="/iscans/:id?" component={IScan} /> }
1617
{ childProps.isDataManager && <Route path="/proteins/:id?" component={Protein} /> }
18+
{ childProps.isDataManager && <Route path="/images/:id?" component={Images} /> }
1719
<Route component={NotFound} />
1820
</Switch>;

client/src/containers/Images.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
form {
2+
margin: auto;
3+
}
4+
5+
.header {
6+
padding: 40px 0;
7+
}
8+
9+
label {
10+
font-weight: 700;
11+
}
12+
13+
.card {
14+
padding-bottom: 10px;
15+
}
16+
17+
.card-header {
18+
font-size: x-large;
19+
font-weight: bold;
20+
}

client/src/containers/Images.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import React, { Component } from "react";
2+
import Slide from "./Slide";
3+
import { Button } from 'react-bootstrap';
4+
import { API } from 'aws-amplify';
5+
import "./Images.css";
6+
7+
export default class Images extends Component {
8+
constructor(props) {
9+
super(props);
10+
11+
this.state = {
12+
caseId: this.props.match.params.id,
13+
images: null,
14+
sync: null,
15+
syncStatus: { status: 'warning', message: 'Checking' },
16+
error: null
17+
};
18+
19+
this.handleRebuild = this.handleRebuild.bind(this);
20+
this.handleReload = this.handleReload.bind(this);
21+
this.handleResync = this.handleResync.bind(this);
22+
this.handleChange = this.handleChange.bind(this);
23+
this.handleSubmit = this.handleSubmit.bind(this);
24+
}
25+
26+
componentDidMount() {
27+
console.log('on component mount');
28+
if (this.state.caseId) {
29+
this.getImage(this.state.caseId);
30+
}
31+
}
32+
33+
async handleReload(event) {
34+
event.preventDefault();
35+
API.put('api', '/pullimages/');
36+
alert('Getting all recent image data. This may take a few minutes to complete.');
37+
}
38+
39+
async handleRebuild(event) {
40+
event.preventDefault();
41+
API.put('api', '/image/' + this.state.images.CaseId)
42+
.then(() => {
43+
this.getImage(this.state.images.CaseId);
44+
});
45+
}
46+
47+
async handleResync(event) {
48+
event.preventDefault();
49+
API.put('api', '/imagesync/' + this.state.images.CaseId)
50+
.then(() => {
51+
this.getImage(this.state.images.CaseId);
52+
});
53+
}
54+
55+
handleSubmit(event) {
56+
event.preventDefault();
57+
this.getImage(this.state.caseId);
58+
}
59+
60+
handleChange(event) {
61+
this.setState({caseId: event.target.value});
62+
}
63+
64+
updateSyncStatus() {
65+
if (this.state.sync) {
66+
if (this.state.sync.lastModified === this.state.images.ModifiedOn) {
67+
if (this.state.sync.syncResult === 200) {
68+
this.setState({ syncStatus: { status: 'success', message: 'Synced'} });
69+
}
70+
else {
71+
this.setState({ syncStatus: { status: 'danger', message: 'Errored' } });
72+
}
73+
}
74+
else {
75+
this.setState({ syncStatus: { status: 'warning', message: 'Pending' } });
76+
}
77+
}
78+
else {
79+
this.setState({ syncStatus: { status: 'warning', message: 'Pending' } });
80+
}
81+
}
82+
83+
clearImageData() {
84+
this.setState({ images: null });
85+
this.setState({ sync: null });
86+
this.setState({ syncStatus: '' });
87+
}
88+
89+
async getImage(caseId) {
90+
this.setState({ error: null });
91+
Promise.all([API.get('api', '/image/' + caseId),
92+
API.get('api', '/imagesync/' + caseId)])
93+
.then(results => {
94+
this.setState({ images: results[0].data });
95+
this.setState({ sync: results[1].data });
96+
this.updateSyncStatus();
97+
this.setState({ caseId: '' });
98+
})
99+
.catch(err => {
100+
if (err.response && err.response.status === 404) {
101+
this.clearImageData();
102+
this.setState({ error: "Image data for '" + caseId + "' could not be found." });
103+
}
104+
});
105+
}
106+
107+
render() {
108+
return (
109+
<div class="container">
110+
<div class="header">
111+
<h1>Images</h1>
112+
<div class="row">
113+
<div class="col-md-6 offset-md-3 col-sm-12">
114+
<form onSubmit={this.handleSubmit}>
115+
<div class="input-group">
116+
<div class="input-group-prepend">
117+
<button type="button" class="btn btn-secondary btn-sm" onClick={this.handleReload}><img src="/sync.png" alt="reload images"></img></button>
118+
</div>
119+
<input id="search" type="text" class="form-control" placeholder="Enter Case Id" value={this.state.caseId} onChange={this.handleChange} />
120+
<div class="input-group-append">
121+
<button id="submit" type="submit" class="btn btn-primary btn-sm">Search</button>
122+
</div>
123+
</div>
124+
</form>
125+
</div>
126+
</div>
127+
</div>
128+
{
129+
this.state.error ?
130+
<div class="alert alert-danger" role="alert">{this.state.error}</div> :
131+
''
132+
}
133+
{
134+
this.state.images ?
135+
<div class="card">
136+
<div class="card-header bg-info text-white text-center">
137+
<Button className="float-left" variant="secondary" size="sm" onClick={this.handleRebuild}><img src="/sync.png" alt="reload image"></img></Button>
138+
{this.state.images.CaseId}
139+
<Button className="float-right" variant={this.state.syncStatus.status} size="sm" onClick={this.handleResync}>{this.state.syncStatus.message}</Button>
140+
</div>
141+
<div class="card-body">
142+
<div class="row">
143+
<label class="col-sm-2">Last Modified</label>
144+
<div class="col-sm-4">{this.state.images.ModifiedOn}</div>
145+
</div>
146+
<h3>Slides</h3>
147+
{this.state.images.Slides.map((item) =>
148+
<Slide content={item} key={item.SlideId} />
149+
)}
150+
</div>
151+
</div>
152+
:
153+
''
154+
}
155+
</div>
156+
);
157+
}
158+
}

client/src/containers/Slide.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
form {
2+
margin: auto;
3+
}
4+
5+
.header {
6+
padding: 40px 0;
7+
}
8+
9+
label {
10+
font-weight: 700;
11+
}
12+
13+
.card {
14+
margin-bottom: 10px;
15+
}
16+
17+
.card-header {
18+
font-size: x-large;
19+
font-weight: bold;
20+
}

client/src/containers/Slide.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React, {Component} from 'react';
2+
import SlideImage from "./SlideImage";
3+
import "./Slide.css";
4+
5+
export default class Slide extends Component {
6+
render() {
7+
return (
8+
<div class="card">
9+
<div class="card-header bg-primary text-white">
10+
<div class="row">
11+
<div class="col-sm-12">{this.props.content.SlideId}</div>
12+
</div>
13+
</div>
14+
<div class="card-body">
15+
{this.props.content.Images.map((item) =>
16+
<SlideImage content={item} key={item.ImageId} />
17+
)}
18+
</div>
19+
</div>
20+
);
21+
}
22+
}

client/src/containers/SlideImage.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
form {
2+
margin: auto;
3+
}
4+
5+
.header {
6+
padding: 40px 0;
7+
}
8+
9+
label {
10+
font-weight: 700;
11+
}
12+
13+
.card {
14+
padding-bottom: 10px;
15+
}
16+
17+
.card-header {
18+
font-size: x-large;
19+
font-weight: bold;
20+
}

client/src/containers/SlideImage.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React, {Component} from 'react';
2+
import "./SlideImage.css";
3+
4+
export default class SlideImage extends Component {
5+
render() {
6+
return (
7+
<div class="row no-gutters">
8+
<label class="col-sm-1">Image Id</label>
9+
<div class="col-sm-2 white-space">{this.props.content.ImageId}</div>
10+
<label class="col-sm-2">Last Modified</label>
11+
<div class="col-sm-3 white-space">{this.props.content.lastModified}</div>
12+
<label class="col-sm-1">Scan Date</label>
13+
<div class="col-sm-3 white-space">{this.props.content.scanDate}</div>
14+
</div>
15+
);
16+
}
17+
}

dynamo/module.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,36 @@ var dynamoModule = (() => {
257257
Item: syncData
258258
}).promise();
259259
}
260+
},
261+
262+
images: {
263+
update: async (imageData) => {
264+
return docClient.put({
265+
TableName: process.env.IMAGES,
266+
Item: imageData
267+
}).promise();
268+
},
269+
270+
get: (caseId) => {
271+
return docClient.get({
272+
TableName: process.env.IMAGES,
273+
Key: { CaseId: caseId }
274+
}).promise();
275+
},
276+
277+
getSync: (caseId) => {
278+
return docClient.get({
279+
TableName: process.env.IMAGES_SYNC,
280+
Key: { CaseId: caseId }
281+
}).promise();
282+
},
283+
284+
updateSync: (syncData) => {
285+
return docClient.put({
286+
TableName: process.env.IMAGES_SYNC,
287+
Item: syncData
288+
}).promise();
289+
}
260290
}
261291
};
262292
})();

0 commit comments

Comments
 (0)