Commit 84bc6514 authored by Marco Sehrer's avatar Marco Sehrer
Browse files

Merge pull request #130 from ninjaconcept/130-reaction-sample-calculations

Sample & Reaction Calculations
parents dcac3c7c 756aacb6
......@@ -45,17 +45,37 @@ class Material extends Component {
</td>
<td>{material.name}</td>
<td>{material.molecule.sum_formular}</td>
<td className="padding-right">
<td>
<NumeralInputWithUnits
key={material.id}
value={material.amount_mg}
unit='mg'
numeralFormat='0,0.0000'
onChange={(amount) => this.handleAmountChange(amount)}
/>
</td>
<td>
<NumeralInputWithUnits
key={material.id}
value={material.amount_value}
unit={material.amount_unit || 'mg'}
units={['mg', 'ml', 'mmol']}
numeralFormat='0,0.00'
convertValueFromUnitToNextUnit={(unit, nextUnit, value) => this.handleUnitChange(unit, nextUnit, value)}
value={material.amount_ml}
unit='ml'
numeralFormat='0,0.0000'
onChange={(amount) => this.handleAmountChange(amount)}
/>
</td>
<td>
<NumeralInputWithUnits
key={material.id}
value={material.amount_mmol}
unit='mmol'
numeralFormat='0,0.0000'
onChange={(amount) => this.handleAmountChange(amount)}
/>
</td>
<td className="padding-right">
<Input
type="text"
......
......@@ -11,9 +11,11 @@ export default class MaterialGroup extends Component {
<th width="5%"></th>
<th width="5%">Ref</th>
<th width="20%">Name</th>
<th width="20%">Molecule</th>
<th width="25%">Amount</th>
<th width="20%">Equi</th>
<th width="15%">Molecule</th>
<th width="15%">mg</th>
<th width="15%">ml</th>
<th width="15%">mmol</th>
<th width="10%">Equi</th>
<th width="5%"></th>
</thead>
<tbody>
......
......@@ -82,5 +82,5 @@ export default class NumeralInputWithUnits extends Component {
NumeralInputWithUnits.defaultProps = {
value: 0,
numeralFormat: '',
units: ['']
units: []
};
......@@ -73,28 +73,28 @@ export default class ReactionDetails extends React.Component {
updatedReactionForAmountChange(changeEvent) {
let {sampleID, amount} = changeEvent;
let sample = this.findSampleById(sampleID);
let updatedSample = this.findSampleById(sampleID);
sample.amount_value = amount.value;
sample.amount_unit = amount.unit;
// normalize to milligram
updatedSample.setAmountAndNormalizeToMilligram(amount.value, amount.unit);
return this.updatedReactionWithSample(this.updatedSamplesForAmountChange.bind(this), sample)
return this.updatedReactionWithSample(this.updatedSamplesForAmountChange.bind(this), updatedSample)
}
updatedReactionForEquivalentChange(changeEvent) {
let {sampleID, equivalent} = changeEvent;
let sample = this.findSampleById(sampleID);
let updatedSample = this.findSampleById(sampleID);
sample.equivalent = equivalent;
updatedSample.equivalent = equivalent;
return this.updatedReactionWithSample(this.updatedSamplesForEquivalentChange.bind(this), sample)
return this.updatedReactionWithSample(this.updatedSamplesForEquivalentChange.bind(this), updatedSample)
}
updatedReactionWithSample(updateFunction, sample) {
updatedReactionWithSample(updateFunction, updatedSample) {
let {reaction} = this.state;
reaction.starting_materials = updateFunction(reaction.starting_materials, sample);
reaction.reactants = updateFunction(reaction.reactants, sample);
reaction.products = updateFunction(reaction.products, sample);
reaction.starting_materials = updateFunction(reaction.starting_materials, updatedSample);
reaction.reactants = updateFunction(reaction.reactants, updatedSample);
reaction.products = updateFunction(reaction.products, updatedSample);
return reaction;
}
......@@ -111,21 +111,23 @@ export default class ReactionDetails extends React.Component {
return samples.map((sample) => {
if (sample.id == updatedSample.id) {
sample.amount_value = updatedSample.amount_value;
sample.amount_unit = updatedSample.amount_unit;
if (referenceSample) {
if (! updatedSample.reference && referenceSample.amount_value) {
sample.equivalent = sample.amount_value / referenceSample.amount_value;
sample.setAmountAndNormalizeToMilligram(updatedSample.amount_value, updatedSample.amount_unit);
if(referenceSample) {
if(!updatedSample.reference && referenceSample.amount_value) {
sample.equivalent = sample.amount_mmol / referenceSample.amount_mmol;
} else {
sample.equivalent = 1.0;
}
}
}
else {
if (updatedSample.reference) {
if (sample.equivalent) {
sample.amount_value = sample.equivalent * updatedSample.amount_value;
if(updatedSample.reference) {
if(sample.equivalent) {
sample.setAmountAndNormalizeToMilligram(sample.equivalent * updatedSample.amount_mmol, 'mmol');
}
}
}
......@@ -139,11 +141,11 @@ export default class ReactionDetails extends React.Component {
return samples.map((sample) => {
if (sample.id == updatedSample.id) {
sample.equivalent = updatedSample.equivalent;
if (referenceSample && referenceSample.amount_value) {
sample.amount_value = updatedSample.equivalent * referenceSample.amount_value;
if(referenceSample && referenceSample.amount_value) {
sample.setAmountAndNormalizeToMilligram(updatedSample.equivalent * referenceSample.amount_mmol, 'mmol');
}
else if (sample.amount_value) {
sample.amount_value = updatedSample.equivalent * sample.amount_value;
else if(sample.amount_value) {
sample.setAmountAndNormalizeToMilligram(updatedSample.equivalent * sample.amount_mmol, 'mmol');
}
}
return sample;
......@@ -157,10 +159,10 @@ export default class ReactionDetails extends React.Component {
sample.reference = true;
}
else {
if (sample.amount_value) {
let referenceAmount = referenceSample.amount_value;
if (referenceSample && referenceAmount) {
sample.equivalent = sample.amount_value / referenceAmount;
if(sample.amount_value) {
let referenceAmount = referenceSample.amount_mmol;
if(referenceSample && referenceAmount) {
sample.equivalent = sample.amount_mmol / referenceAmount;
}
}
sample.reference = false;
......@@ -245,11 +247,12 @@ export default class ReactionDetails extends React.Component {
const svgPath = (reaction.reactionSvg) ? "/images/reactions/" + reaction.reactionSvg : "";
const svgContainerStyle = {
position: 'relative',
height: 0,
width: '100%',
//height: 0,
//width: '100%',
padding: 0,
paddingBottom: '50%'
paddingBottom: '30%'
};
return (
<div>
<Panel header="Reaction Details" bsStyle='primary'>
......@@ -261,7 +264,7 @@ export default class ReactionDetails extends React.Component {
</td>
<td width="70%">
<div style={svgContainerStyle}>
<SVG key={reaction.reactionSvg} src={svgPath}/>
<SVG key={reaction.reactionSvg} src={svgPath} className="molecule-small"/>
</div>
</td>
</tr>
......
......@@ -99,8 +99,8 @@ export default class SampleDetails extends React.Component {
handleAmountChanged(amount) {
let sample = this.state.sample;
sample.amount_unit = amount.unit;
sample.amount_value = amount.value;
sample.setAmountAndNormalizeToMilligram(amount.value, amount.unit);
this.setState({
sample: sample
});
......@@ -534,16 +534,38 @@ export default class SampleDetails extends React.Component {
sampleAmount(sample) {
if(sample.is_scoped == false || sample.amount_value || sample.id == '_new_' ) {
return (
<NumeralInputWithUnits
key={sample.id}
value={sample.amount_value}
unit={sample.amount_unit || 'mg'}
label="Amount"
units={['mg', 'ml', 'mmol']}
numeralFormat='0,0.00'
convertValueFromUnitToNextUnit={(unit, nextUnit, value) => this.handleUnitChanged(unit, nextUnit, value)}
onChange={(amount) => this.handleAmountChanged(amount)}
/>
<table><tr>
<td>
<NumeralInputWithUnits
key={sample.id}
value={sample.amount_mg}
unit='mg'
label="mg"
numeralFormat='0,0.00'
onChange={(amount) => this.handleAmountChanged(amount)}
/>
</td>
<td>
<NumeralInputWithUnits
key={sample.id}
value={sample.amount_ml}
unit='ml'
label="ml"
numeralFormat='0,0.00'
onChange={(amount) => this.handleAmountChanged(amount)}
/>
</td>
<td>
<NumeralInputWithUnits
key={sample.id}
value={sample.amount_mmol}
unit='mmol'
label="mmol"
numeralFormat='0,0.00'
onChange={(amount) => this.handleAmountChanged(amount)}
/>
</td>
</tr></table>
)
} else {
return (
......@@ -558,6 +580,7 @@ export default class SampleDetails extends React.Component {
<Input type="text" label="Purity"
ref="purityInput"
value={sample.purity}
numeralFormat='0,0.00'
onChange={(e) => this.handlePurityChanged(e)}
/>
)
......
......@@ -9,6 +9,10 @@ import ReactionSvgFetcher from '../fetchers/ReactionSvgFetcher';
import UIActions from '../actions/UIActions';
import ScreensFetcher from '../fetchers/ScreensFetcher';
import Molecule from '../models/Molecule';
import Sample from '../models/Sample';
import Reaction from '../models/Reaction';
class ElementActions {
fetchSampleById(id) {
......@@ -68,7 +72,7 @@ class ElementActions {
fetchReactionById(id) {
ReactionsFetcher.fetchById(id)
.then((result) => {
this.dispatch(result.reaction);
this.dispatch(result);
}).catch((errorMessage) => {
console.log(errorMessage);
});
......@@ -144,7 +148,7 @@ class ElementActions {
}
generateEmptySample() {
let sample = {
let sample = new Sample({
id: '_new_',
type: 'sample',
name: 'New Sample',
......@@ -157,7 +161,7 @@ class ElementActions {
location: '',
molfile: '',
molecule: {}
}
})
this.dispatch(sample)
}
......
import 'whatwg-fetch';
import Reaction from '../models/Reaction';
// TODO: Extract common base functionality into ElementsFetcher
export default class ReactionsFetcher {
......@@ -9,7 +10,7 @@ export default class ReactionsFetcher {
.then((response) => {
return response.json()
}).then((json) => {
return json;
return new Reaction(json.reaction);
}).catch((errorMessage) => {
console.log(errorMessage);
});
......@@ -26,7 +27,7 @@ export default class ReactionsFetcher {
.then((response) => {
return response.json().then((json) => {
return {
elements: json.reactions,
elements: json.reactions.map((r) => new Reaction(r)),
totalElements: response.headers.get('X-Total'),
page: response.headers.get('X-Page'),
pages: response.headers.get('X-Total-Pages')
......
import Sample from '../models/Sample';
export default class Reaction {
constructor(args) {
Object.assign(this, args);
}
get starting_materials() {
return this._starting_materials
}
set starting_materials(samples) {
this._starting_materials = samples.map(s => new Sample(s))
}
get reactants() {
return this._reactants
}
set reactants(samples) {
this._reactants = samples.map(s => new Sample(s))
}
get products() {
return this._products
}
set products(samples) {
this._products = samples.map(s => new Sample(s))
}
}
import Molecule from './molecule';
import Molecule from './Molecule';
export default class Sample {
......@@ -7,15 +7,107 @@ export default class Sample {
}
get name() {
//console.log(`Sample(${this.id}).name`)
return this._name
}
set name(name) {
//console.log(`Sample(${this.id}).name=${name}`)
this._name = name
}
get amount() {
return({
value: amount_value,
unit: amount_unit
})
}
setAmountAndNormalizeToMilligram(amount_value, amount_unit) {
this.amount_value = this.convertToMilligram(amount_value, amount_unit)
this.amount_unit = 'mg'
}
get amount_value() {
return this._amount_value || 0;
}
set amount_value(amount_value) {
this._amount_value = amount_value
}
get amount_unit() {
return this._amount_unit || 'mg';
}
set amount_unit(amount_unit) {
this._amount_unit = amount_unit
}
get amount_mg() {
return this.convertMilligramToUnit(this.amount_value, 'mg')
}
get amount_ml() {
return this.convertMilligramToUnit(this.amount_value, 'ml')
}
get amount_mmol() {
return this.convertMilligramToUnit(this.amount_value, 'mmol')
}
//Menge in mmol = Menge (mg) * Reinheit / Molmasse (g/mol)
//Volumen (ml) = Menge (mg) / Dichte (g/ml)
//Menge (mg) = Volumen (ml) * Dichte
//Menge (mg) = Menge (mmol) * Molmasse / Reinheit
convertMilligramToUnit(amount_mg, unit) {
switch (unit) {
case 'mg':
return amount_mg;
break;
case 'ml':
return amount_mg / this.molecule_density;
break;
case 'mmol':
return amount_mg * this.purity / this.molecule_molecular_weight;
break;
default:
return amount_mg
}
}
convertToMilligram(amount_value, amount_unit) {
switch (amount_unit) {
case 'mg':
return amount_value;
break;
case 'ml':
return amount_value * this.molecule_density;
break;
case 'mmol':
return amount_value / this.purity * this.molecule_molecular_weight;
break;
default:
return amount_value
}
}
get molecule_density() {
return this.molecule && this.molecule.density || 1.0
}
get molecule_molecular_weight() {
return this.molecule && this.molecule.molecular_weight
}
get purity() {
return this._purity || 1.0
}
set purity(purity) {
this._purity = purity
}
get molecule() {
return this._molecule
}
......
......@@ -21,7 +21,7 @@
}
.molecule-mid svg {
width: 180px;
width: 180px;
height:180px;
margin-left: 10px;
}
......
......@@ -14,7 +14,7 @@ module SVG
width = (@starting_materials.size + @products.size) * 100 + arrow_width
@template = <<-END
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:cml="http://www.xml-cml.org/schema"
width="#{width}" height="100" viewBox="0 0 #{width} 100" style=" position: absolute; height: 100%; width: 100%; left: 0; top: 0;">
width="#{width}" height="100" viewBox="0 0 #{width} 100" style=" position: absolute; height: 100%; max-height: 200px; width: 100%;">
<title>Reaction 1</title>
END
@plus = <<-END
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment