Commit 9771d7b0 authored by pierre.tremouilhac's avatar pierre.tremouilhac
Browse files

Merge branch '261-report-molecule-with-user-defined-serial' into 'development'

Resolve "Report - molecule with user defined serial"

Closes #261

add user defined label to each sample in report (eg 1, 2, 3a, 3b)

See merge request ComPlat/chemotion_ELN!458
parents 155edbf5 4363a69e
......@@ -217,6 +217,7 @@ module Chemotion
requires :splSettings, type: Array[Hash], coerce_with: ->(val) { JSON.parse(val) }
requires :rxnSettings, type: Array[Hash], coerce_with: ->(val) { JSON.parse(val) }
requires :configs, type: Array[Hash], coerce_with: ->(val) { JSON.parse(val) }
requires :molSerials, type: Array[Hash], coerce_with: ->(val) { JSON.parse(val) }
requires :imgFormat, type: String, default: 'png', values: %w(png eps emf)
requires :fileName, type: String, default: "ELN_Report_" + Time.now.strftime("%Y-%m-%dT%H-%M-%S")
requires :template, type: String, default: "standard"
......@@ -226,6 +227,7 @@ module Chemotion
spl_settings = hashize(params[:splSettings])
rxn_settings = hashize(params[:rxnSettings])
configs = hashize(params[:configs])
mol_serials = params[:molSerials].map(&:to_hash)
attributes = {
file_name: params[:fileName],
......@@ -233,6 +235,7 @@ module Chemotion
configs: configs,
sample_settings: spl_settings,
reaction_settings: rxn_settings,
mol_serials: mol_serials,
objects: params[:objTags],
img_format: params[:imgFormat],
template: params[:template],
......
import React from 'react'
import ReactDOM from 'react-dom'
import Quill from 'quill'
import _ from 'lodash';
export default class QuillViewer extends React.Component {
constructor (props) {
......@@ -13,6 +14,14 @@ export default class QuillViewer extends React.Component {
this.initQuill();
}
componentWillReceiveProps(nextProps) {
const oldVal = this.props.value;
const newVal = nextProps.value;
if (oldVal && newVal && !_.isEqual(newVal, oldVal)) {
this.viewer.setContents(newVal);
}
}
initQuill() {
if (!this.viewer) {
const quillViewer = ReactDOM.findDOMNode(this.refs.quillViewer);
......
......@@ -136,6 +136,10 @@ class ReportActions {
reset() {
return null;
}
updMSVal(moleculeId, value) {
return { moleculeId, value };
}
}
export default alt.createActions(ReportActions);
import React, { Component } from 'react';
export default class CommonInput extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value,
};
this.onBlur = this.onBlur.bind(this);
this.onChange = this.onChange.bind(this);
}
onBlur(e) {
this.props.onCompleteEdit(e.target.value);
}
onChange(e) {
this.setState({ value: e.target.value });
}
render() {
const { placeholder } = this.props;
const { value } = this.state;
return (
<input
value={value || ''}
placeholder={placeholder || ''}
onBlur={this.onBlur}
onChange={this.onChange}
/>
);
}
}
......@@ -101,7 +101,6 @@ const clickToDelete = (e, archive) => {
const deleteBtn = (archive) => {
const onClickToDelete = e => clickToDelete(e, archive);
const downloadable = archive.downloadable;
const deleteTP = (
<Tooltip id="delete-tp">
Delete this archive.
......@@ -116,7 +115,7 @@ const deleteBtn = (archive) => {
</OverlayTrigger>
);
return downloadable ? btn : null;
return btn;
};
const clickToClone = (e, archive) => {
......@@ -128,7 +127,6 @@ const clickToClone = (e, archive) => {
const cloneBtn = (archive) => {
const onClickToClone = e => clickToClone(e, archive);
const downloadable = archive.downloadable;
const cloneTP = (
<Tooltip id="clone-tp">
Load date from this report.
......@@ -143,7 +141,7 @@ const cloneBtn = (archive) => {
</OverlayTrigger>
);
return downloadable ? btn : null;
return btn;
};
export default Archives;
const paramize = (state) => {
const { selectedObjs, splSettings, rxnSettings, configs,
fileName, fileDescription, imgFormat, template } = state;
const { selectedObjs, splSettings, rxnSettings, configs, selMolSerials,
fileName, fileDescription, imgFormat, template } = state;
const params = {
objTags: JSON.stringify(objTags(selectedObjs)),
splSettings: JSON.stringify(abstractSplSettings(splSettings)),
rxnSettings: JSON.stringify(rxnSettings),
configs: JSON.stringify(abstractConfigs(configs)),
imgFormat: imgFormat,
fileName: fileName,
fileDescription: fileDescription,
template: template,
}
imgFormat,
fileName,
fileDescription,
template,
molSerials: JSON.stringify(selMolSerials),
};
return params;
};
......
......@@ -29,34 +29,39 @@ const StdPreviews = ({selectedObjs, splSettings, rxnSettings, configs}) => {
)
}
const SiPreviews = ({selectedObjs, configs}) => {
const configs_pairs = objToKeyValPairs(configs);
const SiPreviews = ({ selectedObjs, configs, molSerials }) => {
const configsPairs = objToKeyValPairs(configs);
return (
<div>
<p>Experimental Part:</p>
<br/>
<br />
<p>[1 Versions] Version InCHI (), Version SMILES()</p>
<br/>
<br />
<p>[2 General remarks]</p>
<br/>
<br />
<p>[3 General procedures]</p>
<SectionSiProcedures selectedObjs={selectedObjs} />
<br/>
<br />
<p>[4 Synthesis]</p>
<SectionSiSynthesis selectedObjs={selectedObjs} configs={configs_pairs} />
<br/>
<SectionSiSynthesis
selectedObjs={selectedObjs}
configs={configsPairs}
molSerials={molSerials}
/>
<br />
<p>[5 Spectra and Copies]</p>
<br/>
<br />
</div>
);
}
};
const Previews = ({selectedObjs, splSettings, rxnSettings, configs, template}) => {
const Previews = ({selectedObjs, splSettings, rxnSettings, configs, template, molSerials}) => {
const content = template === 'supporting_information'
? <SiPreviews
selectedObjs={selectedObjs}
configs={configs}
molSerials={molSerials}
/>
: <StdPreviews
selectedObjs={selectedObjs}
......
......@@ -6,6 +6,7 @@ import UIStore from '../stores/UIStore';
import Setting from './Setting';
import Previews from './Previews';
import Orders from './Orders';
import Serials from './Serials';
import Archives from './Archives';
import Config from './Config';
import PanelHeader from '../common/PanelHeader';
......@@ -98,7 +99,9 @@ export default class ReportContainer extends Component {
render() {
const { splSettings, checkedAllSplSettings, archives, activeKey,
rxnSettings, checkedAllRxnSettings, imgFormat, fileName, template,
configs, checkedAllConfigs, selectedObjs } = this.state;
configs, checkedAllConfigs, selectedObjs, selMolSerials } = this.state;
const archivesTitle = this.archivesTitle();
return (
<Panel
header={this.panelHeader()}
......@@ -134,7 +137,12 @@ export default class ReportContainer extends Component {
<Orders selectedObjs={selectedObjs} template={template} />
</div>
</Tab>
<Tab eventKey={3} title={'Preview'}>
<Tab eventKey={3} title={'Label'}>
<div className="panel-fit-screen">
<Serials selMolSerials={selMolSerials} template={template} />
</div>
</Tab>
<Tab eventKey={4} title={'Preview'}>
<div className="panel-fit-screen">
<Previews
selectedObjs={selectedObjs}
......@@ -142,10 +150,11 @@ export default class ReportContainer extends Component {
rxnSettings={rxnSettings}
configs={configs}
template={template}
molSerials={selMolSerials}
/>
</div>
</Tab>
<Tab eventKey={4} title={this.archivesTitle()}>
<Tab eventKey={5} title={archivesTitle}>
<div className="panel-fit-screen">
<Archives archives={archives} />
</div>
......
import React from 'react';
import { SVGContent, DescriptionContent } from './SectionReaction';
import _ from 'lodash';
import { SVGContent } from './SectionReaction';
import QuillViewer from '../QuillViewer';
import { digit } from '../utils/MathUtils';
import { rmOpsRedundantSpaceBreak, frontBreak } from '../utils/quillFormat';
import ArrayUtils from '../utils/ArrayUtils';
import { Alphabet } from '../utils/ElementUtils';
import _ from 'lodash';
const onlyBlank = (target) => {
if (target.length === 0) return true;
......@@ -25,25 +25,36 @@ const sampleMoleculeName = (s) => {
return null;
};
const Title = ({ el, counter }) => {
let iupacs = el.products.map((p, i) => {
const key1 = `${i}-text`;
const key2 = `${i}-slash`;
const userSerial = (molecule, molSerials = []) => {
let output = 'xx';
molSerials.forEach((ms) => {
if (ms.mol.id === molecule.id && ms.value) output = ms.value;
});
return output;
};
const deltaUserSerial = (molecule, molSerials) => {
const insert = userSerial(molecule, molSerials);
return { insert };
};
const Title = ({ el, counter, molSerials }) => {
let title = [];
el.products.forEach((p, i) => {
const us = userSerial(p.molecule, molSerials);
const key = `${i}-text`;
const comma = <span key={`${i}-comma`}>, </span>;
const smn = sampleMoleculeName(p);
if (smn) {
return [<span key={key1}>{smn}</span>,
<span key={key2}> / </span>];
}
return [<span key={key1}>"<b>NAME</b>"</span>,
<span key={key2}> / </span>];
title = smn
? [...title, <span key={key}>{smn} ({us})</span>, comma]
: [...title, <span key={key}>"<b>NAME</b>"</span>, comma];
});
iupacs = _.flatten(iupacs).slice(0, -1);
title = _.flatten(title).slice(0, -1);
return (
<p>
<span>[4.{counter}] </span>
{iupacs}
<span> (<b>xx</b>)</span>
<span>{title}</span>
</p>
);
};
......@@ -56,100 +67,109 @@ const deltaSampleMoleculeName = (s) => {
return { attributes: { bold: 'true' }, insert: '"NAME"' };
};
const boldXX = () => {
return { attributes: { bold: "true" }, insert: "xx" };
}
const ProductsInfo = ({products = []}) => {
const ProductsInfo = ({ products = [] }) => {
let content = [];
products.forEach( (p, i) => {
products.forEach((p) => {
let ea = [];
const m = p.molecule;
p.elemental_compositions.forEach(ec => {
if(ec.description === "By molecule formula") {
p.elemental_compositions.forEach((ec) => {
if (ec.description === 'By molecule formula') {
for (let [k, v] of Object.entries(ec.data)) {
ea = [...ea, `${k}, ${v}`];
}
}
return null;
});
ea = ea.filter(r => r != null).join("; ");
const cas = p.xref && p.xref.cas ? p.xref.cas.value : "- ";
const pFormula = `Formula: ${m.sum_formular}; `;
const pCAS = `CAS: ${cas}; `;
const pSmiles = `Smiles: ${m.cano_smiles}; `;
const pInCHI = `InCHI: ${m.inchikey}; `;
const pMMass = `Molecular Mass: ${digit(m.molecular_weight, 4)}; `;
const pEMass = `Exact Mass: ${digit(m.exact_molecular_weight, 4)}; `;
const pEA = `EA: ${ea}.`;
content = [...content, { insert: "Name: " }, deltaSampleMoleculeName(p),
{ insert: "; " },
{ insert: pFormula + pCAS + pSmiles +
pInCHI + pMMass + pEMass + pEA },
{ insert: "\n" } ];
ea = ea.filter(r => r != null).join('; ');
const cas = p.xref && p.xref.cas ? p.xref.cas.value : '- ';
const pFormula = `Formula: ${m.sum_formular}; `;
const pCAS = `CAS: ${cas}; `;
const pSmiles = `Smiles: ${m.cano_smiles}; `;
const pInCHI = `InCHI: ${m.inchikey}; `;
const pMMass = `Molecular Mass: ${digit(m.molecular_weight, 4)}; `;
const pEMass = `Exact Mass: ${digit(m.exact_molecular_weight, 4)}; `;
const pEA = `EA: ${ea}.`;
content = [...content,
{ insert: 'Name: ' },
deltaSampleMoleculeName(p),
{ insert: '; ' },
{ insert: pFormula + pCAS + pSmiles + pInCHI + pMMass + pEMass + pEA },
{ insert: '\n' },
];
});
content = content.slice(0,-1);
return <QuillViewer value={{ops: content}} />
}
const stAndReContent = (el, prev_counter, prev_content) => {
let counter = prev_counter;
let content = prev_content;
[...el.starting_materials, ...el.reactants].forEach(el => {
content = content.slice(0, -1);
return <QuillViewer value={{ ops: content }} />;
};
const stAndReContent = (el, prevCounter, prevContent, molSerials) => {
let counter = prevCounter;
let content = prevContent;
[...el.starting_materials, ...el.reactants].forEach((elm) => {
counter += 1;
content = [...content,
{ insert: `{${Alphabet(counter)}|` },
boldXX(),
{ insert: "} " },
deltaSampleMoleculeName(el),
{ insert: ` (${el.amount_g} g, ${digit(el.amount_mol * 1000, 4)} mmol, ${digit(el.equivalent, 2)} equiv.); ` }];
{ insert: `{${Alphabet(counter)}|` },
deltaUserSerial(elm.molecule, molSerials),
{ insert: '} ' },
deltaSampleMoleculeName(elm),
{ insert: ` (${elm.amount_g} g, ${digit(elm.amount_mol * 1000, 4)} mmol, ${digit(elm.equivalent, 2)} equiv.); ` }];
});
return { counter: counter, content: content };
}
return { counter, content };
};
const solventsContent = (el, prev_counter, prev_content) => {
let counter = prev_counter;
let content = prev_content;
el.solvents.forEach(el => {
const solventsContent = (el, prevCounter, prevContent) => {
let counter = prevCounter;
let content = prevContent;
el.solvents.forEach((elm) => {
counter += 1;
content = [...content,
{ insert: `{${Alphabet(counter)}` },
{ insert: "} " },
deltaSampleMoleculeName(el),
{ insert: ` (${digit(el.amount_l * 1000, 2)} mL); ` }];
{ insert: `{${Alphabet(counter)}` },
{ insert: '} ' },
deltaSampleMoleculeName(elm),
{ insert: ` (${digit(elm.amount_l * 1000, 2)} mL); ` }];
});
return { counter: counter, content: content };
}
const porductsContent = (el, prev_counter, prev_content) => {
let counter = prev_counter;
let content = prev_content;
content = [...content, { insert: "Yield: " }];
el.products.forEach(p => {
counter += 1;
return { counter, content };
};
const porductsContent = (el, prevCounter, prevContent, molSerials) => {
let counter = prevCounter;
let content = prevContent;
content = [...content, { insert: 'Yield: ' }];
el.products.forEach((p) => {
const m = p.molecule;
counter += 1;
content = [...content,
{ insert: `{${Alphabet(counter)}|` },
boldXX(),
{ insert: "} " },
{ insert: ` = ${digit(p.equivalent * 100, 0)}%` },
{ insert: ` (${p.amount_g} g, ${digit(p.amount_mol * 1000, 4)} mmol)` },
{ insert: "; " }];
{ insert: `{${Alphabet(counter)}|` },
deltaUserSerial(m, molSerials),
{ insert: '} ' },
{ insert: ` = ${digit(p.equivalent * 100, 0)}%` },
{ insert: ` (${p.amount_g} g, ${digit(p.amount_mol * 1000, 4)} mmol)` },
{ insert: '; ' }];
});
content = content.slice(0,-1);
content = [...content, { insert: "." }];
return { counter: counter, content: content };
}
content = content.slice(0, -1);
content = [...content, { insert: '.' }];
return { counter, content };
};
const materailsContent = (el) => {
let counter = 0;
let content = [];
const stAndRe = stAndReContent(el, counter, content);
const materailsContent = (el, molSerials) => {
const counter = 0;
const content = [];
const stAndRe = stAndReContent(el, counter, content, molSerials);
const solvCon = solventsContent(el, stAndRe.counter, stAndRe.content);
const prodCon = porductsContent(el, solvCon.counter, solvCon.content);
const prodCon = porductsContent(el, solvCon.counter,
solvCon.content, molSerials);
return prodCon.content;
}
};
const tlcContent = (el) => {
let content = [];
if (el.tlc_solvents) {
content = [{ attributes: { italic: 'true' }, insert: 'R' },
{ attributes: { script: 'sub', italic: 'true' }, insert: 'f' },
{ insert: ` = ${el.rf_value} (${el.tlc_solvents}).` }];
}
return content;
};
const obsvTlcContent = (el) => {
let content = [];
......@@ -157,17 +177,7 @@ const obsvTlcContent = (el) => {
content = rmOpsRedundantSpaceBreak(content);
if (onlyBlank(content)) return [];
return frontBreak(content);
}
const tlcContent = (el) => {
let content = [];
if(el.tlc_solvents) {
content = [{ attributes: { italic: "true" }, insert: "R"},
{ attributes: { script: "sub", italic: "true" }, insert: "f"},
{ insert: ` = ${el.rf_value} (${el.tlc_solvents}).`}]
}
return content;
}
};
const rmHeadSpace = (content) => {
let els = content;
......@@ -198,9 +208,7 @@ const rmTailSpace = (content) => {
return els;
};
const opsTailWithSymbol = (els, symbol) => {
return [...els, { insert: symbol }];
};
const opsTailWithSymbol = (els, symbol) => [...els, { insert: symbol }];
const endingSymbol = (content, symbol) => {
if (onlyBlank(content)) return [];
......@@ -215,9 +223,9 @@ const endingSymbol = (content, symbol) => {
const analysesContent = (products) => {
let content = [];
products.map((p) => {
products.forEach((p) => {
const sortAnalyses = ArrayUtils.sortArrByIndex(p.analyses);
return sortAnalyses.map((a) => {
sortAnalyses.forEach((a) => {
const data = a && a.extended_metadata
&& a.extended_metadata.report
&& a.extended_metadata.report === 'true'
......@@ -233,67 +241,62 @@ const analysesContent = (products) => {
};
const dangContent = (el) => {
if(el.dangerous_products.length === 0) return [];
let content = [{ attributes: { bold: "true" }, insert: "Attention! "},
{ insert: "The reaction includes the use of dangerous " +
"chemicals, which have the following " +
"classification: " }];
el.dangerous_products.forEach( d => {
content = [...content, { insert: d }, { insert: ", " }];
if (el.dangerous_products.length === 0) return [];
let content = [{ attributes: { bold: 'true' }, insert: 'Attention! ' },
{ insert: 'The reaction includes the use of dangerous ' +
'chemicals, which have the following ' +
'classification: ' }];
el.dangerous_products.forEach((d) => {
content = [...content, { insert: d }, { insert: ', ' }];
});
content = content.slice(0,-1);
content = content.slice(0, -1);
content = rmOpsRedundantSpaceBreak(content);
return content;
}
};
const DangerBlock = ({el}) => {