diff --git a/app/api/chemotion/reaction_api.rb b/app/api/chemotion/reaction_api.rb index 39a9f1ef5e..cddcb169f0 100644 --- a/app/api/chemotion/reaction_api.rb +++ b/app/api/chemotion/reaction_api.rb @@ -79,7 +79,7 @@ def update_materials_for_reaction(reaction, material_attributes, current_user) subsample.real_amount_value = sample.real_amount_value subsample.real_amount_unit = sample.real_amount_unit subsample.metrics = sample.metrics - + subsample.rf_value = sample.rf_value # add new data container # subsample.container = create_root_container subsample.container = update_datamodel(sample.container) if sample.container @@ -151,6 +151,7 @@ def update_materials_for_reaction(reaction, material_attributes, current_user) existing_sample.short_label = sample.short_label if sample.short_label existing_sample.short_label = fixed_label if fixed_label existing_sample.name = sample.name if sample.name + existing_sample.rf_value = sample.rf_value if sample.rf_value if r = existing_sample.residues[0] r.assign_attributes sample.residues_attributes[0] diff --git a/app/api/chemotion/sample_api.rb b/app/api/chemotion/sample_api.rb index 2dbab60735..3d1066f0db 100644 --- a/app/api/chemotion/sample_api.rb +++ b/app/api/chemotion/sample_api.rb @@ -281,6 +281,7 @@ class SampleAPI < Grape::API optional :location, type: String, desc: "Sample location" optional :molfile, type: String, desc: "Sample molfile" optional :sample_svg_file, type: String, desc: "Sample SVG file" + optional :rf_value, type: Array[Hash], desc: "Sample rf value" # optional :molecule, type: Hash, desc: "Sample molecule" do # optional :id, type: Integer # end @@ -397,6 +398,7 @@ class SampleAPI < Grape::API optional :molarity_unit, type: String, desc: "Sample real amount_unit" requires :description, type: String, desc: "Sample description" requires :purity, type: Float, desc: "Sample purity" + optional :rf_value, type: Array[Hash], desc: "Sample rf value" # requires :solvent, type: String, desc: "Sample solvent" optional :solvent, type: Array[Hash], desc: "Sample solvent" requires :location, type: String, desc: "Sample location" @@ -435,6 +437,7 @@ class SampleAPI < Grape::API target_amount_unit: params[:target_amount_unit], real_amount_value: params[:real_amount_value], real_amount_unit: params[:real_amount_unit], + rf_value: params[:rf_value], molarity_value: params[:molarity_value], molarity_unit: params[:molarity_unit], description: params[:description], diff --git a/app/api/entities/sample_attr_entity.rb b/app/api/entities/sample_attr_entity.rb index 5f291bcb83..67c734b4ff 100644 --- a/app/api/entities/sample_attr_entity.rb +++ b/app/api/entities/sample_attr_entity.rb @@ -12,7 +12,7 @@ class SampleAttrEntity < Grape::Entity :pubchem_tag, :xref, :code_log, :can_update, :can_copy, :can_publish, :molecule_name_hash, #:molecule_computed_props, :showed_name, :user_labels, :decoupled, - :molecular_mass, :sum_formula + :molecular_mass, :sum_formula, :rf_value def created_at object.created_at.strftime("%d.%m.%Y, %H:%M") diff --git a/app/api/helpers/report_helpers.rb b/app/api/helpers/report_helpers.rb index 63ee5f7e57..5369226406 100644 --- a/app/api/helpers/report_helpers.rb +++ b/app/api/helpers/report_helpers.rb @@ -479,7 +479,8 @@ def build_sql_reaction_sample(columns, c_id, ids, checkedAll = false) updated_at: ['s.updated_at', nil, 0], # deleted_at: ['wp.deleted_at', nil, 10], molecule_name: ['mn."name"', '"molecule name"', 1], - molarity_value: ['s."molarity_value"', '"molarity_value"', 0] + molarity_value: ['s."molarity_value"', '"molarity_value"', 0], + rf_value: ['s."rf_value"', '"rf_value"', 0] }, sample_id: { external_label: ['s.external_label', '"sample external label"', 0], diff --git a/app/models/sample.rb b/app/models/sample.rb index ca0810d1aa..97b94cca54 100644 --- a/app/models/sample.rb +++ b/app/models/sample.rb @@ -42,6 +42,7 @@ # molecular_mass :float # sum_formula :string # solvent :jsonb +# rf_value :jsonb # # Indexes # diff --git a/app/packs/src/components/ReactionDetails.js b/app/packs/src/components/ReactionDetails.js index 38a0657df9..eabf5fee8c 100644 --- a/app/packs/src/components/ReactionDetails.js +++ b/app/packs/src/components/ReactionDetails.js @@ -58,6 +58,8 @@ export default class ReactionDetails extends Component { this.handleSubmit = this.handleSubmit.bind(this); this.onTabPositionChanged = this.onTabPositionChanged.bind(this); this.handleSegmentsChange = this.handleSegmentsChange.bind(this); + this.handleTlcChange = this.handleTlcChange.bind(this); + if(!reaction.reaction_svg_file) { this.updateReactionSvg(); } @@ -153,6 +155,11 @@ export default class ReactionDetails extends Component { this.handleReactionChange(newReaction, options); } + handleTlcChange() { + const { reaction } = this.state; + this.handleReactionChange(reaction, false); + } + handleProductClick(product) { const uri = Aviator.getCurrentURI(); const uriArray = uri.split(/\//); @@ -407,14 +414,14 @@ export default class ReactionDetails extends Component { reaction={reaction} onReactionChange={(reaction, options) => this.handleReactionChange(reaction, options)} onInputChange={(type, event) => this.handleInputChange(type, event)} - /> + /> ), properties: ( this.handleReactionChange(r)} + onChange={this.handleTlcChange} onInputChange={(type, event) => this.handleInputChange(type, event)} key={reaction.checksum} /> diff --git a/app/packs/src/components/ReactionDetailsProperties.js b/app/packs/src/components/ReactionDetailsProperties.js index 2ea10ddb2c..2774028f25 100644 --- a/app/packs/src/components/ReactionDetailsProperties.js +++ b/app/packs/src/components/ReactionDetailsProperties.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Row, Col, FormGroup, ControlLabel, FormControl, MenuItem, - ListGroupItem, ListGroup, InputGroup, DropdownButton + ListGroupItem, ListGroup, InputGroup, DropdownButton, OverlayTrigger, Tooltip } from 'react-bootstrap'; import Select from 'react-select'; import 'moment-precise-range-plugin'; @@ -13,6 +13,8 @@ import StringTag from './StringTag'; import { solventsTL } from './utils/reactionPredefined'; import OlsTreeSelect from './OlsComponent'; import { permitOn } from './common/uis'; +import ReactionTlcSection from './ReactionTlcSection'; +import Sample from './models/Sample'; export default class ReactionDetailsProperties extends Component { constructor(props) { @@ -60,7 +62,7 @@ export default class ReactionDetailsProperties extends Component { } render() { - const { reaction } = this.props; + const { reaction, onChange } = this.props; const solventsItems = solventsTL.map((x, i) => { const val = Object.keys(x)[0]; return ( @@ -76,6 +78,53 @@ export default class ReactionDetailsProperties extends Component { ); + const oldTlcRow = (reaction.rf_value || reaction.tlc_solvents) ? ( + + please enter this solvent data to the tlc section above of the respective sample}> + + + Solvents (parts) + + + + { solventsItems } + + this.props.onInputChange('tlc_solvents', event)} + /> + + + + + + please enter this rf value to the tlc section above of the respective sample}> + + + RF Value + this.props.onInputChange('rfValue', event)} + /> + + + + + ) : null; + return (
@@ -109,57 +158,25 @@ export default class ReactionDetailsProperties extends Component { + -

TLC-Control

+

TLC Control

- - - Solvents (parts) - - - - { solventsItems } - - this.props.onInputChange('tlc_solvents', event)} - /> - - - - - - - Rf-Value - this.props.onInputChange('rfValue', event)} - /> - - + + {oldTlcRow} - TLC-Description + TLC Description this.props.onInputChange('tlcDescription', event)} /> @@ -175,5 +192,6 @@ export default class ReactionDetailsProperties extends Component { ReactionDetailsProperties.propTypes = { reaction: PropTypes.object, onReactionChange: PropTypes.func, - onInputChange: PropTypes.func + onInputChange: PropTypes.func, + onChange: PropTypes.func.isRequired }; diff --git a/app/packs/src/components/ReactionTlcDetails.js b/app/packs/src/components/ReactionTlcDetails.js new file mode 100644 index 0000000000..582e06f958 --- /dev/null +++ b/app/packs/src/components/ReactionTlcDetails.js @@ -0,0 +1,88 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Tab, Tabs } from 'react-bootstrap'; +import Reaction from './models/Reaction'; +import SampleTlcControl from './SampleTlcControl'; + +// eslint-disable-next-line react/prefer-stateless-function +export default class ReactionTlcDetails extends Component { + + constructor(props) { + super(props); + + const { reaction } = props; + this.state = { + // eslint-disable-next-line object-shorthand + reaction: reaction + }; + } + + // eslint-disable-next-line class-methods-use-this + title(sample) { + const moleculeLabel = sample.molecule_name_hash ? sample.molecule_name_hash.label : sample.molecule_iupac_name; + // eslint-disable-next-line prefer-template + const moleculeLabelMiniString = moleculeLabel ? moleculeLabel.substring(0, 12) + '...' : moleculeLabel; + const sampleTabLabel = (moleculeLabel && moleculeLabel.length <= 12) ? moleculeLabel : moleculeLabelMiniString; + return ( + + {sampleTabLabel} + + ); + } + + allReactionMaterials() { + const { products, reactants, starting_materials } = this.state.reaction; + const respectiveSamples = []; + const { onChange, tabType } = this.props; + if (tabType === 'Starting Materials') { + respectiveSamples.push(...starting_materials); + } else if (tabType === 'Reactants') { + respectiveSamples.push(...reactants); + } else { + respectiveSamples.push(...products); + } + + const tabs = respectiveSamples.map((sample) => { + return ( + + + + ); + }); + + return ( + + {tabs} + + ); + } + + render() { + return ( +
+ {this.allReactionMaterials()} +
+ ); + } +} + +ReactionTlcDetails.propTypes = { + reaction: PropTypes.instanceOf(Reaction).isRequired, + onChange: PropTypes.func.isRequired, + tabType: PropTypes.string.isRequired +}; + diff --git a/app/packs/src/components/ReactionTlcSection.js b/app/packs/src/components/ReactionTlcSection.js new file mode 100644 index 0000000000..2dfb24a606 --- /dev/null +++ b/app/packs/src/components/ReactionTlcSection.js @@ -0,0 +1,57 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Tabs, Tab } from 'react-bootstrap'; +import Reaction from './models/Reaction'; +import ReactionTlcDetails from './ReactionTlcDetails'; + +export default class ReactionTlcSection extends Component { + constructor(props) { + super(props); + + const { reaction } = props; + this.state = { + reaction: reaction + }; + } + + sampleType() { + const { reaction, onChange } = this.props; + const sampleType = ['Starting Materials', 'Reactants', 'Products']; + + const eachTab = sampleType.map((index) => { + return ( + + + + ); + }); + + return ( + + {eachTab} + + ); + } + + render() { + return ( +
+ {this.sampleType()} +
+ ); + } +} + +ReactionTlcSection.propTypes = { + reaction: PropTypes.instanceOf(Reaction).isRequired, + onChange: PropTypes.func.isRequired +}; + diff --git a/app/packs/src/components/SampleForm.js b/app/packs/src/components/SampleForm.js index efa4bdb2bc..a54976ae1c 100644 --- a/app/packs/src/components/SampleForm.js +++ b/app/packs/src/components/SampleForm.js @@ -13,6 +13,7 @@ import { solventOptions } from './staticDropdownOptions/options'; import SampleDetailsSolvents from './SampleDetailsSolvents'; import PrivateNoteElement from './PrivateNoteElement'; import NotificationActions from './actions/NotificationActions'; +import SampleTlcControl from './SampleTlcControl'; export default class SampleForm extends React.Component { @@ -605,6 +606,16 @@ export default class SampleForm extends React.Component { + + + + + + + + : ; + return ( + + + + ); + } + + deleteTlcColumn(solvIndex) { + const { sample } = this.state; + sample.deleteTlcColumn(solvIndex); + this.props.onChange(sample); + } + + addTlcColumn() { + const { sample } = this.state; + this.props.onChange(sample); + sample.addTlcColumn(); + } + + handleFieldChanged(field, e) { + const { sample } = this.props; + sample[field] = e; + this.props.onChange(sample); + } + + render() { + const { + sample + } = this.props; + + const style = { + paddingTop: '5px', + }; + + return ( +
+ + + { this.tlcCollapseBtn() } + +
+ +
+
+
+
+
+ ); + } +} + +SampleTlcControl.propTypes = { + sample: PropTypes.instanceOf(Sample).isRequired, + onChange: PropTypes.func.isRequired +}; diff --git a/app/packs/src/components/SampleTlcDetails.js b/app/packs/src/components/SampleTlcDetails.js new file mode 100644 index 0000000000..d6ef11e34e --- /dev/null +++ b/app/packs/src/components/SampleTlcDetails.js @@ -0,0 +1,129 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button, FormControl, Glyphicon, OverlayTrigger, Tooltip } from 'react-bootstrap'; +import Sample from './models/Sample'; + +const TlcColumnDetails = ({sample, onChangeRfValue, solvIndex, rfValue, deleteTlcColumn +}) => { + + const rfArray = sample.rf_value; + + const changeRfTlcValue = (event, type) => { + rfValue = event.target.value; + onChangeRfValue(rfValue, solvIndex, type); + }; + + const obe = rfArray[solvIndex]; + return ( + + + changeRfTlcValue(event, 'tlcVal')} + placeholder="select" + value={obe ? obe.tlcVal : ''} + > + + + + + + + + changeRfTlcValue(event, 'rfVal')} + /> + + + + + + ); +}; + +const TlcGroup = ({ + sample, deleteTlcColumn, onChangeRfValue +}) => { + const contents = []; + const sampleRfArray = sample.rf_value; + if (sampleRfArray && sampleRfArray.length != 0) { + for (let key = 0; key < sampleRfArray.length; key++) { + contents.push(( + + )); + } + } + return ( + + + Tlc-Solvent + Rf-Value + + {contents.map(item => item)} + + ); +}; + +// eslint-disable-next-line react/prefer-stateless-function +class SampleTlcDetails extends React.Component { + + constructor(props) { + super(props); + } + + render() { + const { + sample, deleteTlcColumn, addTlcColumn, onChangeRfValue + } = this.props; + + return ( + + + + + + + +
+ Add new Rf-value}> + + +
+ ); + } +} + +export default SampleTlcDetails; + +SampleTlcDetails.propTypes = { + sample: PropTypes.instanceOf(Sample).isRequired, + deleteTlcColumn: PropTypes.func.isRequired, + addTlcColumn: PropTypes.func.isRequired, + onChangeRfValue: PropTypes.func.isRequired +}; diff --git a/app/packs/src/components/contextActions/ModalExport.js b/app/packs/src/components/contextActions/ModalExport.js index 4f097ace9f..64e2f24c5a 100644 --- a/app/packs/src/components/contextActions/ModalExport.js +++ b/app/packs/src/components/contextActions/ModalExport.js @@ -64,6 +64,7 @@ export default class ModalExport extends React.Component { {value: "updated_at", text: "updated at", checked: false}, {value: "user_labels", text: "user labels", checked: false}, {value: "literature", text: "literature", checked: false}, + {value: "rf_value", text: "RF Value", checked: false} ], molecule: [ {value: "cano_smiles", text: "canonical smiles", checked: true}, diff --git a/app/packs/src/components/fetchers/ReactionsFetcher.js b/app/packs/src/components/fetchers/ReactionsFetcher.js index 420ebc3f4a..c909656dd4 100644 --- a/app/packs/src/components/fetchers/ReactionsFetcher.js +++ b/app/packs/src/components/fetchers/ReactionsFetcher.js @@ -58,8 +58,8 @@ export default class ReactionsFetcher { }, body: JSON.stringify(reaction.serialize()) }).then(response => response.json()) - .then(json => GenericElsFetcher.uploadGenericFiles(reaction, json.reaction.id, 'Reaction') - .then(() => this.fetchById(json.reaction.id))).catch((errorMessage) => { + .then(json => GenericElsFetcher.uploadGenericFiles(reaction, reaction.id, 'Reaction') + .then(() => this.fetchById(reaction.id))).catch((errorMessage) => { console.log(errorMessage); }); diff --git a/app/packs/src/components/fetchers/SamplesFetcher.js b/app/packs/src/components/fetchers/SamplesFetcher.js index efcad0ca64..4aceda902c 100644 --- a/app/packs/src/components/fetchers/SamplesFetcher.js +++ b/app/packs/src/components/fetchers/SamplesFetcher.js @@ -72,8 +72,9 @@ export default class SamplesFetcher { }, body: JSON.stringify(sample.serialize()) }).then(response => response.json()) - .then(json => GenericElsFetcher.uploadGenericFiles(sample, json.sample.id, 'Sample') - .then(() => this.fetchById(json.sample.id))).catch((errorMessage) => { + .then(json => GenericElsFetcher.uploadGenericFiles(sample, sample.id, 'Sample') + .then(() => this.fetchById(sample.id))).catch((errorMessage) => { + console.log(sample); console.log(errorMessage); }); if (files.length > 0) { diff --git a/app/packs/src/components/models/Sample.js b/app/packs/src/components/models/Sample.js index 4354aff6fc..76d9449383 100644 --- a/app/packs/src/components/models/Sample.js +++ b/app/packs/src/components/models/Sample.js @@ -306,6 +306,7 @@ export default class Sample extends Element { boiling_point_lowerbound: this.boiling_point_lowerbound, melting_point_upperbound: this.melting_point_upperbound, melting_point_lowerbound: this.melting_point_lowerbound, + rf_value: this.rf_value, residues: this.residues, elemental_compositions: this.elemental_compositions, is_split: this.is_split || false, @@ -332,6 +333,20 @@ export default class Sample extends Element { this._is_top_secret = is_top_secret; } + get rf_value() { + try { + const jsonrf = JSON.parse(this._rf_value); + const rfVal = []; + rfVal.push(jsonrf); + } + catch (e) {} + return this._rf_value; + } + + set rf_value(rf_value) { + this._rf_value = rf_value; + } + set contains_residues(value) { this._contains_residues = value; if(value) { @@ -1064,6 +1079,51 @@ export default class Sample extends Element { } this.solvent = tmpSolvents } + + deleteTlcColumn(solvIndex) { + + const tmpRfArray = []; + if (this._rf_value) { + Object.assign(tmpRfArray, this._rf_value); + } + tmpRfArray.splice(solvIndex, 1); + this._rf_value= tmpRfArray; + } + + addTlcColumn() { + const object = { + rfVal: '', + tlcVal: '-' + }; + if (!this._rf_value) { + this._rf_value = []; + this._rf_value.push(object); + } else if (this._rf_value) { + this._rf_value.push(object); + } + } + + updateRfValue(rfValue, solvIndex, type) { + + const tmpRfArray = []; + + Object.assign(tmpRfArray, this._rf_value); + const obe = this._rf_value[solvIndex]; + + let object; + if (type === 'rfVal') { + object = { + rfVal: rfValue, + tlcVal: obe.tlcVal + }; + } else if (type === 'tlcVal') { + object = { + rfVal: obe.rfVal, + tlcVal: rfValue + }; + } + this._rf_value[solvIndex] = object; + } } Sample.counter = 0; diff --git a/app/packs/src/components/report/SectionReaction.js b/app/packs/src/components/report/SectionReaction.js index fa797b05b2..e89cbeb4ee 100644 --- a/app/packs/src/components/report/SectionReaction.js +++ b/app/packs/src/components/report/SectionReaction.js @@ -283,7 +283,7 @@ const TLCContent = ({show, tlcDescription, tlcSolvents, rfValue}) => (

TLC - Control

-            Rf-value: {rfValue} (Solvent: {tlcSolvents})
+            RF Value: {rfValue} (Solvent: {tlcSolvents})
           
diff --git a/app/serializers/detail_levels/sample.rb b/app/serializers/detail_levels/sample.rb index 3055f71b65..8d6845b3a6 100644 --- a/app/serializers/detail_levels/sample.rb +++ b/app/serializers/detail_levels/sample.rb @@ -10,7 +10,7 @@ def base_attributes :reaction_description, :container, :pubchem_tag, :xref, :code_log, :metrics, :can_update, :can_copy, :can_publish, :molecule_name_hash, # :molecule_computed_props, :showed_name, :decoupled, - :molecular_mass, :sum_formula + :molecular_mass, :sum_formula, :rf_value ] end diff --git a/db/migrate/20211201160140_add_rf_value_to_samples.rb b/db/migrate/20211201160140_add_rf_value_to_samples.rb new file mode 100644 index 0000000000..3df0415eff --- /dev/null +++ b/db/migrate/20211201160140_add_rf_value_to_samples.rb @@ -0,0 +1,5 @@ +class AddRfValueToSamples < ActiveRecord::Migration[5.2] + def change + add_column :samples, :rf_value, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index 68d5f5c4ca..034a0e87f9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_03_09_182512) do +ActiveRecord::Schema.define(version: 2022_04_04_111722) do # These are extensions that must be enabled in order to support this database enable_extension "hstore" @@ -914,6 +914,7 @@ t.float "molecular_mass" t.string "sum_formula" t.jsonb "solvent" + t.jsonb "rf_value" t.index ["deleted_at"], name: "index_samples_on_deleted_at" t.index ["identifier"], name: "index_samples_on_identifier" t.index ["molecule_id"], name: "index_samples_on_sample_id" diff --git a/lib/export/export_json.rb b/lib/export/export_json.rb index 41e4cce37b..2ec9aa63f7 100644 --- a/lib/export/export_json.rb +++ b/lib/export/export_json.rb @@ -162,7 +162,7 @@ def sample_sql , s.molarity_value, s.molarity_unit , s.created_at, s.updated_at, s.description , s.purity, s.solvent, s.impurities, s.location, s.is_top_secret - , s.external_label, s.short_label + , s.external_label, s.short_label, s.rf_value, , s.imported_readout, s.sample_svg_file, s.identifier , s.density, s.melting_point as melting_point , s.boiling_point as boiling_point, s.stereo diff --git a/lib/import/import_collections.rb b/lib/import/import_collections.rb index 5266aebeb8..930e3e1559 100644 --- a/lib/import/import_collections.rb +++ b/lib/import/import_collections.rb @@ -157,6 +157,7 @@ def import_samples 'location', 'is_top_secret', 'external_label', + 'rf_value', 'short_label', 'real_amount_value', 'real_amount_unit', diff --git a/lib/import/import_json.rb b/lib/import/import_json.rb index d98e7aa508..0e274bd661 100644 --- a/lib/import/import_json.rb +++ b/lib/import/import_json.rb @@ -352,7 +352,7 @@ def filter_attributes(klass) when 'Sample' attributes -= [ 'ancestry', 'molecule_id', 'xref', 'fingerprint_id', 'molecule_name_id', - 'is_top_secret', 'molecule_svg_file' + 'is_top_secret', 'molecule_svg_file', 'rf_value' ] attributes += %w[residues_attributes elemental_compositions_attributes molecule_name_attributes] # when 'Reaction' diff --git a/lib/import/import_samples.rb b/lib/import/import_samples.rb index 8ef08a1876..9d0425ae04 100644 --- a/lib/import/import_samples.rb +++ b/lib/import/import_samples.rb @@ -229,6 +229,7 @@ def excluded_fields # 'external_label', 'created_by', 'short_label', + 'rf_value', # 'real_amount_value', # 'real_amount_unit', # 'imported_readout',