@@ -439,9 +366,8 @@ export default function ReactionVariations({ reaction, onReactionChange }) {
context={{
copyRow,
removeRow,
- columnDefinitions,
setColumnDefinitions,
- reactionHasPolymers: reaction.hasPolymers(),
+ reactionHasPolymers,
reactionShortLabel: reaction.short_label,
allReactionAnalyses
}}
diff --git a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsAnalyses.js b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsAnalyses.js
index 80b494cbad..1825062ede 100644
--- a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsAnalyses.js
+++ b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsAnalyses.js
@@ -1,8 +1,7 @@
import React, { useState } from 'react';
-import { AgGridReact } from 'ag-grid-react';
import PropTypes from 'prop-types';
import {
- Form, Button, Modal, Badge, Tooltip
+ Form, Button, Modal, Badge
} from 'react-bootstrap';
import cloneDeep from 'lodash/cloneDeep';
import Reaction from 'src/models/Reaction';
diff --git a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents.js b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents.js
deleted file mode 100644
index 1a24e0dba1..0000000000
--- a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents.js
+++ /dev/null
@@ -1,215 +0,0 @@
-/* eslint-disable react/display-name */
-import React, { useState } from 'react';
-import { AgGridReact } from 'ag-grid-react';
-import {
- Button, ButtonGroup, Badge, Modal, Form, OverlayTrigger, Tooltip
-} from 'react-bootstrap';
-import PropTypes from 'prop-types';
-import {
- getVariationsRowName,
- convertUnit
-} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils';
-import {
- updateNonReferenceMaterialOnMassChange,
- getReferenceMaterial, getMolFromGram, getGramFromMol
-} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials';
-import { parseNumericString } from 'src/utilities/MathUtils';
-
-function RowToolsCellRenderer({
- data: variationsRow, context
-}) {
- const { reactionShortLabel, copyRow, removeRow } = context;
- return (
-
- {getVariationsRowName(reactionShortLabel, variationsRow.id)}
-
-
-
-
-
- );
-}
-
-RowToolsCellRenderer.propTypes = {
- data: PropTypes.shape({
- id: PropTypes.number.isRequired,
- }).isRequired,
- context: PropTypes.shape({
- reactionShortLabel: PropTypes.string.isRequired,
- copyRow: PropTypes.func.isRequired,
- removeRow: PropTypes.func.isRequired,
- }).isRequired,
-};
-
-function EquivalentFormatter({ value: cellData }) {
- const { equivalent } = cellData.aux;
-
- return `${Number(equivalent).toPrecision(4)}`;
-}
-
-function EquivalentParser({ data: variationsRow, oldValue: cellData, newValue }) {
- let equivalent = parseNumericString(newValue);
- if (equivalent < 0) {
- equivalent = 0;
- }
- // Adapt mass to updated equivalent.
- const referenceMaterial = getReferenceMaterial(variationsRow);
- const referenceMol = getMolFromGram(referenceMaterial.mass.value, referenceMaterial);
- const mass = getGramFromMol(referenceMol * equivalent, cellData);
-
- // Adapt amount to updated equivalent.
- const amount = getMolFromGram(mass, cellData);
-
- return {
- ...cellData,
- mass: { ...cellData.mass, value: mass },
- amount: { ...cellData.amount, value: amount },
- aux: { ...cellData.aux, equivalent }
- };
-}
-
-function PropertyFormatter({ value: cellData, colDef }) {
- const { displayUnit } = colDef.currentEntryWithDisplayUnit;
- const valueInDisplayUnit = convertUnit(Number(cellData.value), cellData.unit, displayUnit);
-
- return `${Number(valueInDisplayUnit).toPrecision(4)}`;
-}
-
-function PropertyParser({
- oldValue: cellData, newValue, colDef
-}) {
- const { entry, displayUnit } = colDef.currentEntryWithDisplayUnit;
- let value = parseNumericString(newValue);
- if (entry !== 'temperature' && value < 0) {
- value = 0;
- }
- value = convertUnit(value, displayUnit, cellData.unit);
- const updatedCellData = { ...cellData, value };
-
- return updatedCellData;
-}
-
-function MaterialFormatter({ value: cellData, colDef }) {
- const { entry, displayUnit } = colDef.currentEntryWithDisplayUnit;
- const valueInDisplayUnit = convertUnit(Number(cellData[entry].value), cellData[entry].unit, displayUnit);
-
- return `${Number(valueInDisplayUnit).toPrecision(4)}`;
-}
-
-function MaterialParser({
- data: variationsRow, oldValue: cellData, newValue, colDef, context
-}) {
- const { field } = colDef;
- const { entry, displayUnit } = colDef.currentEntryWithDisplayUnit;
- const columnGroup = field.split('.')[0];
- let value = convertUnit(parseNumericString(newValue), displayUnit, cellData[entry].unit);
- if (value < 0) {
- value = 0;
- }
- let updatedCellData = { ...cellData, [entry]: { ...cellData[entry], value } };
-
- if (entry === 'mass') {
- // Adapt amount to updated mass.
- const amount = getMolFromGram(value, updatedCellData);
- updatedCellData = { ...updatedCellData, amount: { ...updatedCellData.amount, value: amount } };
- }
- if (entry === 'amount') {
- // Adapt mass to updated amount.
- const mass = getGramFromMol(value, updatedCellData);
- updatedCellData = { ...updatedCellData, mass: { ...updatedCellData.mass, value: mass } };
- }
- // See comment in ReactionVariationsUtils.updateVariationsRow() regarding reactive updates.
- if (updatedCellData.aux.isReference) {
- return updatedCellData;
- }
- return updateNonReferenceMaterialOnMassChange(
- variationsRow,
- updatedCellData,
- columnGroup,
- context.reactionHasPolymers
- );
-}
-
-function NoteCellRenderer(props) {
- return (
-
- double click to edit
-
- }
- >
- {props.value ? props.value : '_'}
-
- );
-}
-
-function NoteCellEditor({
- data: variationsRow,
- value,
- onValueChange,
- stopEditing,
- context
-}) {
- const [note, setNote] = useState(value);
- const { reactionShortLabel } = context;
-
- const onClose = () => {
- stopEditing();
- };
-
- const onSave = () => {
- onValueChange(note);
- stopEditing();
- };
-
- const cellContent = (
-
-
- {`Edit note for ${getVariationsRowName(reactionShortLabel, variationsRow.id)}`}
-
-
- setNote(event.target.value)}
- />
-
-
-
-
-
- );
-
- return cellContent;
-}
-
-NoteCellEditor.propTypes = {
- data: PropTypes.shape({
- id: PropTypes.number.isRequired,
- }).isRequired,
- value: PropTypes.string.isRequired,
- onValueChange: PropTypes.func.isRequired,
- stopEditing: PropTypes.func.isRequired,
- context: PropTypes.shape({
- reactionShortLabel: PropTypes.string.isRequired,
- }).isRequired,
-};
-
-export {
- RowToolsCellRenderer,
- EquivalentFormatter,
- EquivalentParser,
- PropertyFormatter,
- PropertyParser,
- MaterialFormatter,
- MaterialParser,
- NoteCellRenderer,
- NoteCellEditor
-};
diff --git a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.js b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.js
new file mode 100644
index 0000000000..ceb2d789ac
--- /dev/null
+++ b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.js
@@ -0,0 +1,513 @@
+/* eslint-disable react/display-name */
+import React, { useState, useEffect } from 'react';
+import { AgGridReact } from 'ag-grid-react';
+import {
+ Button, ButtonGroup, Badge, Modal, Form, OverlayTrigger, Tooltip
+} from 'react-bootstrap';
+import PropTypes from 'prop-types';
+import {
+ getVariationsRowName, convertUnit, getStandardUnits
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils';
+import {
+ getReferenceMaterial, getCatalystMaterial, getFeedstockMaterial, getMolFromGram, getGramFromMol,
+ computeEquivalent, computePercentYield
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials';
+import { parseNumericString } from 'src/utilities/MathUtils';
+import {
+ calculateGasMoles, calculateTON, calculateFeedstockMoles, calculateFeedstockVolume
+} from 'src/utilities/UnitsConversion';
+
+function RowToolsCellRenderer({
+ data: variationsRow, context
+}) {
+ const { reactionShortLabel, copyRow, removeRow } = context;
+ return (
+
+ {getVariationsRowName(reactionShortLabel, variationsRow.id)}
+
+
+
+
+
+ );
+}
+
+RowToolsCellRenderer.propTypes = {
+ data: PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ }).isRequired,
+ context: PropTypes.shape({
+ reactionShortLabel: PropTypes.string.isRequired,
+ copyRow: PropTypes.func.isRequired,
+ removeRow: PropTypes.func.isRequired,
+ }).isRequired,
+};
+
+function EquivalentParser({ data: variationsRow, oldValue: cellData, newValue }) {
+ let equivalent = parseNumericString(newValue);
+ if (equivalent < 0) {
+ equivalent = 0;
+ }
+ // Adapt mass to updated equivalent.
+ const referenceMaterial = getReferenceMaterial(variationsRow);
+ const referenceMol = getMolFromGram(referenceMaterial.mass.value, referenceMaterial);
+ const mass = getGramFromMol(referenceMol * equivalent, cellData);
+
+ // Adapt amount to updated equivalent.
+ const amount = getMolFromGram(mass, cellData);
+
+ return {
+ ...cellData,
+ mass: { ...cellData.mass, value: mass },
+ amount: { ...cellData.amount, value: amount },
+ equivalent: { ...cellData.equivalent, value: equivalent }
+ };
+}
+
+function PropertyFormatter({ value: cellData, colDef }) {
+ const { displayUnit } = colDef.entryDefs;
+ const valueInDisplayUnit = convertUnit(Number(cellData.value), cellData.unit, displayUnit);
+
+ return `${Number(valueInDisplayUnit).toPrecision(4)}`;
+}
+
+function PropertyParser({
+ oldValue: cellData, newValue, colDef
+}) {
+ const { currentEntry, displayUnit } = colDef.entryDefs;
+ let value = parseNumericString(newValue);
+ if (currentEntry !== 'temperature' && value < 0) {
+ value = 0;
+ }
+ value = convertUnit(value, displayUnit, cellData.unit);
+ const updatedCellData = { ...cellData, value };
+
+ return updatedCellData;
+}
+
+function MaterialFormatter({ value: cellData, colDef }) {
+ const { currentEntry, displayUnit } = colDef.entryDefs;
+ const valueInDisplayUnit = convertUnit(
+ Number(cellData[currentEntry].value),
+ cellData[currentEntry].unit,
+ displayUnit
+ );
+
+ return `${Number(valueInDisplayUnit).toPrecision(4)}`;
+}
+
+function MaterialParser({
+ data: variationsRow, oldValue: cellData, newValue, colDef, context
+}) {
+ const { currentEntry, displayUnit } = colDef.entryDefs;
+ let value = convertUnit(parseNumericString(newValue), displayUnit, cellData[currentEntry].unit);
+ if (value < 0) {
+ value = 0;
+ }
+ let updatedCellData = { ...cellData, [currentEntry]: { ...cellData[currentEntry], value } };
+
+ if (currentEntry === 'mass') {
+ // Adapt amount to updated mass.
+ const amount = getMolFromGram(value, updatedCellData);
+ updatedCellData = { ...updatedCellData, amount: { ...updatedCellData.amount, value: amount } };
+ }
+ if (currentEntry === 'amount') {
+ // Adapt mass to updated amount.
+ const mass = getGramFromMol(value, updatedCellData);
+ updatedCellData = { ...updatedCellData, mass: { ...updatedCellData.mass, value: mass } };
+ }
+ if (updatedCellData.aux.isReference) {
+ return updatedCellData;
+ }
+
+ const referenceMaterial = getReferenceMaterial(variationsRow);
+
+ // Adapt equivalent to updated mass.
+ if ('equivalent' in updatedCellData) {
+ const equivalent = computeEquivalent(updatedCellData, referenceMaterial);
+ updatedCellData = { ...updatedCellData, equivalent: { ...updatedCellData.equivalent, value: equivalent } };
+ }
+
+ // Adapt yield to updated mass.
+ if ('yield' in updatedCellData) {
+ const percentYield = computePercentYield(updatedCellData, referenceMaterial, context.reactionHasPolymers);
+ updatedCellData = { ...updatedCellData, yield: { ...updatedCellData.yield, value: percentYield } };
+ }
+
+ return updatedCellData;
+}
+
+function GasParser({
+ data: variationsRow, oldValue: cellData, newValue, colDef
+}) {
+ const { currentEntry, displayUnit } = colDef.entryDefs;
+ let value = convertUnit(parseNumericString(newValue), displayUnit, cellData[currentEntry].unit);
+ if (currentEntry !== 'temperature' && value < 0) {
+ value = 0;
+ }
+ let updatedCellData = { ...cellData, [currentEntry]: { ...cellData[currentEntry], value } };
+
+ switch (currentEntry) {
+ case 'concentration':
+ case 'temperature': {
+ const temperatureInKelvin = convertUnit(
+ updatedCellData.temperature.value,
+ updatedCellData.temperature.unit,
+ 'K'
+ );
+
+ const concentration = updatedCellData.concentration.value;
+ const { vesselVolume } = updatedCellData.aux;
+ const amount = calculateGasMoles(vesselVolume, concentration, temperatureInKelvin);
+ const mass = getGramFromMol(amount, updatedCellData);
+
+ const catalyst = getCatalystMaterial(variationsRow);
+ const catalystAmount = catalyst?.amount.value ?? 0;
+ const turnoverNumber = calculateTON(amount, catalystAmount);
+
+ const feedstockPurity = getFeedstockMaterial(variationsRow)?.aux.purity || 1;
+ const feedstockAmount = calculateFeedstockMoles(vesselVolume, feedstockPurity);
+ const percentYield = (amount / feedstockAmount) * 100;
+
+ updatedCellData = {
+ ...updatedCellData,
+ mass: { ...updatedCellData.mass, value: mass },
+ amount: { ...updatedCellData.amount, value: amount },
+ yield: { ...updatedCellData.yield, value: percentYield },
+ turnoverNumber: { ...updatedCellData.turnoverNumber, value: turnoverNumber },
+ };
+ break;
+ }
+ default:
+ break;
+ }
+
+ const durationInHours = convertUnit(
+ updatedCellData.duration.value,
+ updatedCellData.duration.unit,
+ 'Hour(s)'
+ );
+ const turnoverNumber = updatedCellData.turnoverNumber.value;
+ const turnoverFrequency = turnoverNumber / (durationInHours || 1);
+
+ return {
+ ...updatedCellData,
+ turnoverFrequency: { ...updatedCellData.turnoverFrequency, value: turnoverFrequency }
+ };
+}
+
+function FeedstockParser({
+ data: variationsRow, oldValue: cellData, newValue, colDef
+}) {
+ const { currentEntry, displayUnit } = colDef.entryDefs;
+ let value = convertUnit(parseNumericString(newValue), displayUnit, cellData[currentEntry].unit);
+ if (value < 0) {
+ value = 0;
+ }
+ let updatedCellData = { ...cellData, [currentEntry]: { ...cellData[currentEntry], value } };
+
+ switch (currentEntry) {
+ case 'amount': {
+ const amount = updatedCellData.amount.value;
+ const mass = getGramFromMol(amount, updatedCellData);
+
+ const purity = updatedCellData.aux.purity || 1;
+ const volume = calculateFeedstockVolume(amount, purity);
+
+ updatedCellData = {
+ ...updatedCellData,
+ mass: { ...updatedCellData.mass, value: mass },
+ volume: { ...updatedCellData.volume, value: volume },
+ };
+ break;
+ }
+ case 'volume': {
+ const volume = updatedCellData.volume.value;
+ const purity = updatedCellData.aux.purity || 1;
+ const amount = calculateFeedstockMoles(volume, purity);
+
+ const mass = getGramFromMol(amount, updatedCellData);
+
+ updatedCellData = {
+ ...updatedCellData,
+ mass: { ...updatedCellData.mass, value: mass },
+ amount: { ...updatedCellData.amount, value: amount },
+ };
+ break;
+ }
+ case 'equivalent': {
+ return updatedCellData;
+ }
+ default:
+ break;
+ }
+
+ if (updatedCellData.aux.isReference) {
+ return updatedCellData;
+ }
+
+ const referenceMaterial = getReferenceMaterial(variationsRow);
+ const equivalent = computeEquivalent(updatedCellData, referenceMaterial);
+
+ return { ...updatedCellData, equivalent: { ...updatedCellData.equivalent, value: equivalent } };
+}
+
+function NoteCellRenderer(props) {
+ return (
+
+ double click to edit
+
+ )}
+ >
+ {props.value ? props.value : '_'}
+
+ );
+}
+
+function NoteCellEditor({
+ data: variationsRow,
+ value,
+ onValueChange,
+ stopEditing,
+ context
+}) {
+ const [note, setNote] = useState(value);
+ const { reactionShortLabel } = context;
+
+ const onClose = () => {
+ stopEditing();
+ };
+
+ const onSave = () => {
+ onValueChange(note);
+ stopEditing();
+ };
+
+ const cellContent = (
+
+
+ {`Edit note for ${getVariationsRowName(reactionShortLabel, variationsRow.id)}`}
+
+
+ setNote(event.target.value)}
+ />
+
+
+
+
+
+ );
+
+ return cellContent;
+}
+
+NoteCellEditor.propTypes = {
+ data: PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ }).isRequired,
+ value: PropTypes.string.isRequired,
+ onValueChange: PropTypes.func.isRequired,
+ stopEditing: PropTypes.func.isRequired,
+ context: PropTypes.shape({
+ reactionShortLabel: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
+function MaterialOverlay({ value: cellData }) {
+ const { aux = null } = cellData;
+
+ return (
+
+
+ {aux?.isReference && (
+
Reference
+ )}
+ {aux?.coefficient !== null && (
+
{`Coefficient: ${Number(aux.coefficient).toPrecision(4)}`}
+ )}
+ {aux?.molecularWeight !== null && (
+
{`Molar mass: ${Number(aux.molecularWeight).toPrecision(2)} g/mol`}
+ )}
+
+
+ );
+}
+
+MaterialOverlay.propTypes = {
+ value: PropTypes.arrayOf(PropTypes.shape({
+ value: PropTypes.number.isRequired,
+ unit: PropTypes.string.isRequired,
+ })).isRequired,
+ colDef: PropTypes.shape({
+ entryDefs: PropTypes.shape({
+ currentEntry: PropTypes.number.isRequired,
+ displayUnit: PropTypes.string.isRequired,
+ }).isRequired,
+ }).isRequired,
+};
+
+function MenuHeader({
+ column, context, setSort, names, gasType = 'off'
+}) {
+ const { setColumnDefinitions } = context;
+ const [ascendingSort, setAscendingSort] = useState('inactive');
+ const [descendingSort, setDescendingSort] = useState('inactive');
+ const [noSort, setNoSort] = useState('inactive');
+ const [name, setName] = useState(names[0]);
+ const { field, entryDefs } = column.colDef;
+ const { currentEntry, displayUnit, availableEntries } = entryDefs;
+ const units = getStandardUnits(currentEntry);
+ const currentEntryTitle = currentEntry.split(/(?=[A-Z])/).join(' ').toLowerCase(); // e.g. 'turnoverNumber' -> 'turnover number'
+
+ const onSortChanged = () => {
+ setAscendingSort(column.isSortAscending() ? 'sort_active' : 'inactive');
+ setDescendingSort(column.isSortDescending() ? 'sort_active' : 'inactive');
+ setNoSort(
+ !column.isSortAscending() && !column.isSortDescending()
+ ? 'sort_active'
+ : 'inactive'
+ );
+ };
+
+ useEffect(() => {
+ column.addEventListener('sortChanged', onSortChanged);
+ onSortChanged();
+ }, []);
+
+ const onSortRequested = (order, event) => {
+ setSort(order, event.shiftKey);
+ };
+
+ const onUnitChanged = () => {
+ const newDisplayUnit = units[(units.indexOf(displayUnit) + 1) % units.length];
+
+ setColumnDefinitions(
+ {
+ type: 'update_entry_defs',
+ field,
+ entryDefs: { currentEntry, displayUnit: newDisplayUnit, availableEntries },
+ gasType
+ }
+ );
+ };
+
+ const unitSelection = (
+
+ );
+
+ const onEntryChanged = () => {
+ const newCurrentEntry = availableEntries[(availableEntries.indexOf(currentEntry) + 1) % availableEntries.length];
+ const newUnit = getStandardUnits(newCurrentEntry)[0];
+
+ setColumnDefinitions(
+ {
+ type: 'update_entry_defs',
+ field,
+ entryDefs: { currentEntry: newCurrentEntry, displayUnit: newUnit, availableEntries },
+ gasType
+ }
+ );
+ };
+
+ const entrySelection = (
+
+ );
+
+ const sortMenu = (
+
+
onSortRequested('asc', event)}
+ onTouchEnd={(event) => onSortRequested('asc', event)}
+ className={`customSortDownLabel ${ascendingSort}`}
+ >
+
+
+
onSortRequested('desc', event)}
+ onTouchEnd={(event) => onSortRequested('desc', event)}
+ className={`customSortUpLabel ${descendingSort}`}
+ >
+
+
+
onSortRequested('', event)}
+ onTouchEnd={(event) => onSortRequested('', event)}
+ className={`customSortRemoveLabel ${noSort}`}
+ >
+
+
+
+ );
+
+ return (
+
+
setName(names[(names.indexOf(name) + 1) % names.length])}
+ >
+ {`${name} ${gasType !== 'off' ? `(${gasType})` : ''}`}
+
+
+ {entrySelection}
+ {' '}
+ {unitSelection}
+
+ {sortMenu}
+
+ );
+}
+
+MenuHeader.propTypes = {
+ column: PropTypes.instanceOf(AgGridReact.column).isRequired,
+ context: PropTypes.instanceOf(AgGridReact.context).isRequired,
+ setSort: PropTypes.func.isRequired,
+ names: PropTypes.arrayOf(PropTypes.string).isRequired,
+ gasType: PropTypes.string,
+};
+
+MenuHeader.defaultProps = {
+ gasType: 'off',
+};
+
+export {
+ RowToolsCellRenderer,
+ EquivalentParser,
+ PropertyFormatter,
+ PropertyParser,
+ MaterialFormatter,
+ MaterialParser,
+ GasParser,
+ FeedstockParser,
+ NoteCellRenderer,
+ NoteCellEditor,
+ MaterialOverlay,
+ MenuHeader,
+};
diff --git a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.js b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.js
index b4540d122f..d59562521e 100644
--- a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.js
+++ b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.js
@@ -1,11 +1,10 @@
-import { AgGridReact } from 'ag-grid-react';
-import React from 'react';
-import PropTypes from 'prop-types';
-import { cloneDeep } from 'lodash';
+import { get, cloneDeep } from 'lodash';
import {
- convertUnit, materialTypes, volumeUnits, massUnits, amountUnits, getStandardUnit,
- getCellDataType
+ materialTypes, getStandardUnits, getCellDataType, updateColumnDefinitions, getStandardValue
} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils';
+import {
+ MaterialOverlay, MenuHeader
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents';
function getMolFromGram(gram, material) {
if (material.aux.loading) {
@@ -34,6 +33,18 @@ function getReferenceMaterial(variationsRow) {
return Object.values(potentialReferenceMaterials).find((material) => material.aux?.isReference || false);
}
+function getCatalystMaterial(variationsRow) {
+ const variationsRowCopy = cloneDeep(variationsRow);
+ const potentialCatalystMaterials = { ...variationsRowCopy.startingMaterials, ...variationsRowCopy.reactants };
+ return Object.values(potentialCatalystMaterials).find((material) => material.aux?.gasType === 'catalyst' || false);
+}
+
+function getFeedstockMaterial(variationsRow) {
+ const variationsRowCopy = cloneDeep(variationsRow);
+ const potentialFeedstockMaterials = { ...variationsRowCopy.startingMaterials, ...variationsRowCopy.reactants };
+ return Object.values(potentialFeedstockMaterials).find((material) => material.aux?.gasType === 'feedstock' || false);
+}
+
function computeEquivalent(material, referenceMaterial) {
return getMolFromGram(material.mass.value, material)
/ getMolFromGram(referenceMaterial.mass.value, referenceMaterial);
@@ -60,12 +71,17 @@ function getReactionMaterialsIDs(reactionMaterials) {
return Object.values(reactionMaterials).flat().map((material) => material.id);
}
+function getReactionMaterialsGasTypes(reactionMaterials) {
+ return Object.values(reactionMaterials).flat().map((material) => material.gas_type);
+}
+
function updateYields(variationsRow, reactionHasPolymers) {
const updatedVariationsRow = cloneDeep(variationsRow);
const referenceMaterial = getReferenceMaterial(updatedVariationsRow);
- Object.entries(updatedVariationsRow.products).forEach(([productName, productMaterial]) => {
- updatedVariationsRow.products[productName].aux.yield = computePercentYield(
+ Object.values(updatedVariationsRow.products).forEach((productMaterial) => {
+ if (productMaterial.aux.gasType === 'gas') { return; }
+ productMaterial.yield.value = computePercentYield(
productMaterial,
referenceMaterial,
reactionHasPolymers
@@ -80,22 +96,78 @@ function updateEquivalents(variationsRow) {
const referenceMaterial = getReferenceMaterial(updatedVariationsRow);
['startingMaterials', 'reactants'].forEach((materialType) => {
- Object.entries(updatedVariationsRow[materialType]).forEach(([materialName, material]) => {
+ Object.values(updatedVariationsRow[materialType]).forEach((material) => {
if (material.aux.isReference) { return; }
- updatedVariationsRow[materialType][materialName].aux.equivalent = computeEquivalent(material, referenceMaterial);
+ const updatedEquivalent = computeEquivalent(material, referenceMaterial);
+ material.equivalent.value = updatedEquivalent;
});
});
return updatedVariationsRow;
}
-function getMaterialData(material) {
+function getMaterialEntries(materialType, gasType) {
+ switch ((gasType !== 'off') ? gasType : materialType) {
+ case 'solvents':
+ return ['volume'];
+ case 'products':
+ return ['mass', 'amount', 'yield'];
+ case 'startingMaterials':
+ case 'reactants':
+ case 'catalyst':
+ return ['mass', 'amount', 'equivalent'];
+ case 'feedstock':
+ return ['mass', 'amount', 'volume', 'equivalent'];
+ case 'gas':
+ return [
+ 'duration',
+ 'temperature',
+ 'concentration',
+ 'turnoverNumber',
+ 'turnoverFrequency',
+ 'mass',
+ 'amount',
+ 'yield'
+ ];
+ default:
+ return [];
+ }
+}
+
+function cellIsEditable(params) {
+ const entry = params.colDef.entryDefs.currentEntry;
+ const cellData = get(params.data, params.colDef.field);
+ const { isReference, gasType, materialType } = cellData.aux;
+
+ switch (entry) {
+ case 'equivalent':
+ return !isReference;
+ case 'mass':
+ return !['feedstock', 'gas'].includes(gasType);
+ case 'amount':
+ return materialType !== 'products';
+ case 'yield':
+ case 'turnoverNumber':
+ case 'turnoverFrequency':
+ return false;
+ default:
+ return true;
+ }
+}
+
+function getMaterialData(material, materialType, gasMode = false, vesselVolume = null) {
const materialCopy = cloneDeep(material);
- const mass = { value: materialCopy.amount_g ?? null, unit: getStandardUnit('mass') };
- const amount = { value: material.amount_mol ?? null, unit: getStandardUnit('amount') };
- const volume = { value: materialCopy.amount_l ?? null, unit: getStandardUnit('volume') };
+ let gasType = materialCopy.gas_type ?? 'off';
+ gasType = gasMode ? gasType : 'off';
+
+ // Mutable data is represented as "entries", e.g., `foo: {value: bar, unit: baz}.
+ const entries = getMaterialEntries(materialType, gasType);
+ const materialData = entries.reduce((data, entry) => {
+ data[entry] = { value: getStandardValue(entry, materialCopy), unit: getStandardUnits(entry)[0] };
+ return data;
+ }, {});
- const aux = {
+ materialData.aux = {
coefficient: materialCopy.coefficient ?? null,
isReference: materialCopy.reference ?? false,
loading: (Array.isArray(materialCopy.residues) && materialCopy.residues.length) ? materialCopy.residues[0].custom_info?.loading : null,
@@ -103,12 +175,49 @@ function getMaterialData(material) {
molarity: materialCopy.molarity_value ?? null,
molecularWeight: materialCopy.molecule_molecular_weight ?? null,
sumFormula: materialCopy.molecule_formula ?? null,
- yield: null,
- equivalent: null
+ gasType,
+ vesselVolume,
+ materialType,
};
+ return materialData;
+}
+
+function getMaterialColumnGroupChild(material, materialType, headerComponent, gasMode) {
+ const materialCopy = cloneDeep(material);
+
+ let gasType = materialCopy.gas_type ?? 'off';
+ gasType = gasMode ? gasType : 'off';
+
+ const entries = getMaterialEntries(
+ materialType,
+ gasType
+ );
+ const entry = entries[0];
+
+ const names = [`ID: ${materialCopy.id.toString()}`];
+ ['external_label', 'name', 'short_label', 'molecule_formula', 'molecule_iupac_name'].forEach((name) => {
+ if (materialCopy[name]) {
+ names.push(materialCopy[name]);
+ }
+ });
+
return {
- volume, mass, amount, aux
+ field: `${materialType}.${materialCopy.id}`, // Must be unique.
+ tooltipField: `${materialType}.${materialCopy.id}`,
+ tooltipComponent: MaterialOverlay,
+ entryDefs: {
+ currentEntry: entry,
+ displayUnit: getStandardUnits(entry)[0],
+ availableEntries: entries
+ },
+ editable: (params) => cellIsEditable(params),
+ cellDataType: getCellDataType(entry, gasType),
+ headerComponent,
+ headerComponentParams: {
+ names,
+ gasType,
+ },
};
}
@@ -126,13 +235,13 @@ function removeObsoleteMaterialsFromVariations(variations, currentMaterials) {
return updatedVariations;
}
-function addMissingMaterialsToVariations(variations, currentMaterials) {
+function addMissingMaterialsToVariations(variations, currentMaterials, gasMode) {
const updatedVariations = cloneDeep(variations);
updatedVariations.forEach((row) => {
Object.keys(materialTypes).forEach((materialType) => {
currentMaterials[materialType].forEach((material) => {
if (!(material.id in row[materialType])) {
- row[materialType][material.id] = getMaterialData(material);
+ row[materialType][material.id] = getMaterialData(material, materialType, gasMode);
}
});
});
@@ -140,90 +249,40 @@ function addMissingMaterialsToVariations(variations, currentMaterials) {
return updatedVariations;
}
-function MaterialOverlay({
- value: cellData, colDef
-}) {
- const { aux = null } = cellData;
- const { entry, displayUnit } = colDef.currentEntryWithDisplayUnit;
-
- return (
-
-
- {entry !== 'equivalent' && (
-
- {Number(convertUnit(cellData[entry].value, cellData[entry].unit, displayUnit)).toPrecision(4) + " " + displayUnit}
-
- )}
- {aux?.isReference && (
-
Reference
- )}
- {aux?.equivalent !== null && (
-
{"Equivalent: " + Number(aux.equivalent).toPrecision(4)}
- )}
- {aux?.coefficient !== null && (
-
{"Coefficient: " + Number(aux.coefficient).toPrecision(4)}
- )}
- {aux?.yield !== null && (
-
{"Yield: " + Number(aux.yield).toPrecision(4) + "%"}
- )}
- {aux?.molecularWeight !== null && (
-
{"Molar mass: " + Number(aux.molecularWeight).toPrecision(2) + " g/mol"}
- )}
-
-
- );
+function updateVariationsGasTypes(variations, currentMaterials, gasMode) {
+ const updatedVariations = cloneDeep(variations);
+ updatedVariations.forEach((row) => {
+ Object.keys(materialTypes).forEach((materialType) => {
+ currentMaterials[materialType].forEach((material) => {
+ const currentGasType = material.gas_type ?? 'off';
+ if (currentGasType !== row[materialType][material.id].aux.gasType) {
+ row[materialType][material.id] = getMaterialData(material, materialType, gasMode);
+ }
+ });
+ });
+ });
+ return updatedVariations;
}
-MaterialOverlay.propTypes = {
- value: PropTypes.arrayOf(PropTypes.shape({
- value: PropTypes.number.isRequired,
- unit: PropTypes.string.isRequired,
- })).isRequired,
- colDef: PropTypes.shape({
- currentEntryWithDisplayUnit: PropTypes.shape({
- entry: PropTypes.number.isRequired,
- displayUnit: PropTypes.string.isRequired,
- }).isRequired,
- }).isRequired,
-};
+function updateColumnDefinitionsMaterialTypes(columnDefinitions, currentMaterials, gasMode) {
+ let updatedColumnDefinitions = cloneDeep(columnDefinitions);
-function getMaterialColumnGroupChild(material, materialType, headerComponent) {
- const materialCopy = cloneDeep(material);
- let entries = {};
- if (materialType === 'solvents') {
- entries = { volume: volumeUnits };
- }
- if (materialType === 'products') {
- entries = { mass: massUnits };
- }
- if (['startingMaterials', 'reactants'].includes(materialType)) {
- entries = { mass: massUnits, amount: amountUnits };
- if (!materialCopy.reference ?? false) {
- entries.equivalent = [];
- }
- }
- const names = [`ID: ${materialCopy.id.toString()}`];
- ['external_label', 'name', 'short_label', 'molecule_formula', 'molecule_iupac_name'].forEach((name) => {
- if (materialCopy[name]) {
- names.push(materialCopy[name]);
- }
+ Object.entries(currentMaterials).forEach(([materialType, materials]) => {
+ const updatedMaterials = materials.map(
+ (material) => getMaterialColumnGroupChild(material, materialType, MenuHeader, gasMode)
+ );
+ updatedColumnDefinitions = updateColumnDefinitions(
+ updatedColumnDefinitions,
+ materialType,
+ 'children',
+ updatedMaterials
+ );
});
- const entry = materialType === 'solvents' ? 'volume' : 'mass';
- return {
- field: `${materialType}.${materialCopy.id}`, // Must be unique.
- tooltipField: `${materialType}.${materialCopy.id}`,
- tooltipComponent: MaterialOverlay,
- currentEntryWithDisplayUnit: { entry, displayUnit: getStandardUnit(entry) },
- cellDataType: getCellDataType(entry),
- headerComponent,
- headerComponentParams: {
- names,
- entries
- },
- };
+
+ return updatedColumnDefinitions;
}
-function updateColumnDefinitionsMaterials(columnDefinitions, currentMaterials, headerComponent) {
+function updateColumnDefinitionsMaterials(columnDefinitions, currentMaterials, headerComponent, gasMode) {
const updatedColumnDefinitions = cloneDeep(columnDefinitions);
Object.entries(currentMaterials).forEach(([materialType, materials]) => {
@@ -238,7 +297,14 @@ function updateColumnDefinitionsMaterials(columnDefinitions, currentMaterials, h
// Add missing materials.
materials.forEach((material) => {
if (!materialColumnGroup.children.some((child) => child.field === `${materialType}.${material.id}`)) {
- materialColumnGroup.children.push(getMaterialColumnGroupChild(material, materialType, headerComponent));
+ materialColumnGroup.children.push(
+ getMaterialColumnGroupChild(
+ material,
+ materialType,
+ headerComponent,
+ gasMode
+ )
+ );
}
});
});
@@ -246,24 +312,6 @@ function updateColumnDefinitionsMaterials(columnDefinitions, currentMaterials, h
return updatedColumnDefinitions;
}
-function updateNonReferenceMaterialOnMassChange(variationsRow, material, materialType, reactionHasPolymers) {
- const referenceMaterial = getReferenceMaterial(variationsRow);
-
- // Adapt equivalent to updated mass.
- const equivalent = (!material.aux.isReference && ['startingMaterials', 'reactants'].includes(materialType))
- ? computeEquivalent(material, referenceMaterial) : material.aux.equivalent;
-
- // Adapt yield to updated mass.
- const percentYield = (materialType === 'products')
- ? computePercentYield(material, referenceMaterial, reactionHasPolymers) : material.aux.yield;
-
- const updatedAux = { ...material.aux, equivalent, yield: percentYield };
-
- return {
- ...material, aux: updatedAux
- };
-}
-
function updateVariationsRowOnReferenceMaterialChange(row, reactionHasPolymers) {
let updatedRow = cloneDeep(row);
updatedRow = updateEquivalents(updatedRow);
@@ -273,17 +321,23 @@ function updateVariationsRowOnReferenceMaterialChange(row, reactionHasPolymers)
}
export {
- MaterialOverlay,
getMaterialColumnGroupChild,
getReactionMaterials,
getReactionMaterialsIDs,
+ getReactionMaterialsGasTypes,
getMaterialData,
updateColumnDefinitionsMaterials,
- updateNonReferenceMaterialOnMassChange,
+ updateColumnDefinitionsMaterialTypes,
updateVariationsRowOnReferenceMaterialChange,
removeObsoleteMaterialsFromVariations,
addMissingMaterialsToVariations,
+ updateVariationsGasTypes,
getReferenceMaterial,
+ getCatalystMaterial,
+ getFeedstockMaterial,
getMolFromGram,
getGramFromMol,
+ computeEquivalent,
+ computePercentYield,
+ cellIsEditable,
};
diff --git a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsReducers.js b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsReducers.js
new file mode 100644
index 0000000000..da0d832f6c
--- /dev/null
+++ b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsReducers.js
@@ -0,0 +1,65 @@
+import { MenuHeader } from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents';
+import {
+ updateColumnDefinitionsMaterials, updateColumnDefinitionsMaterialTypes
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials';
+import {
+ getCellDataType,
+ updateColumnDefinitions
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils';
+
+function columnDefinitionsReducer(columnDefinitions, action) {
+ switch (action.type) {
+ case 'update_material_set': {
+ return updateColumnDefinitionsMaterials(
+ columnDefinitions,
+ action.reactionMaterials,
+ MenuHeader,
+ action.gasMode
+ );
+ }
+ case 'update_entry_defs': {
+ let updatedColumnDefinitions = updateColumnDefinitions(
+ columnDefinitions,
+ action.field,
+ 'entryDefs',
+ action.entryDefs
+ );
+ updatedColumnDefinitions = updateColumnDefinitions(
+ updatedColumnDefinitions,
+ action.field,
+ 'cellDataType',
+ getCellDataType(action.entryDefs.currentEntry, action.gasType)
+ );
+ return updatedColumnDefinitions;
+ }
+ case 'toggle_gas_mode': {
+ let updatedColumnDefinitions = updateColumnDefinitions(
+ columnDefinitions,
+ 'properties.duration',
+ 'editable',
+ !action.gasMode
+ );
+ updatedColumnDefinitions = updateColumnDefinitionsMaterialTypes(
+ updatedColumnDefinitions,
+ action.reactionMaterials,
+ action.gasMode
+ );
+
+ return updatedColumnDefinitions;
+ }
+ case 'update_gas_type': {
+ return updateColumnDefinitionsMaterialTypes(
+ columnDefinitions,
+ action.reactionMaterials,
+ action.gasMode
+ );
+ }
+ default: {
+ return columnDefinitions;
+ }
+ }
+}
+
+export {
+ columnDefinitionsReducer
+};
diff --git a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.js b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.js
index 209719905d..7df06b79f3 100644
--- a/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.js
+++ b/app/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.js
@@ -10,6 +10,7 @@ const durationUnits = ['Second(s)', 'Minute(s)', 'Hour(s)', 'Day(s)', 'Week(s)']
const massUnits = ['g', 'mg', 'μg'];
const volumeUnits = ['l', 'ml', 'μl'];
const amountUnits = ['mol', 'mmol'];
+const concentrationUnits = ['ppm'];
const materialTypes = {
startingMaterials: { label: 'Starting Materials', reactionAttributeName: 'starting_materials' },
reactants: { label: 'Reactants', reactionAttributeName: 'reactants' },
@@ -17,26 +18,19 @@ const materialTypes = {
solvents: { label: 'Solvents', reactionAttributeName: 'solvents' }
};
-function getStandardUnit(entry) {
- switch (entry) {
- case 'volume':
- return volumeUnits[0];
- case 'mass':
- return massUnits[0];
- case 'amount':
- return amountUnits[0];
- case 'temperature':
- return temperatureUnits[0];
- case 'duration':
- return durationUnits[0];
- default:
- return null;
- }
-}
-
function convertUnit(value, fromUnit, toUnit) {
if (temperatureUnits.includes(fromUnit) && temperatureUnits.includes(toUnit)) {
- return convertTemperature(value, fromUnit, toUnit);
+ const convertedValue = convertTemperature(value, fromUnit, toUnit);
+ if (toUnit === 'K' && convertedValue < 0) {
+ return 0;
+ }
+ if (toUnit === '°C' && convertedValue < -273.15) {
+ return -273.15;
+ }
+ if (toUnit === '°F' && convertedValue < -459.67) {
+ return -459.67;
+ }
+ return convertedValue;
}
if (durationUnits.includes(fromUnit) && durationUnits.includes(toUnit)) {
return convertDuration(value, fromUnit, toUnit);
@@ -57,17 +51,70 @@ function convertUnit(value, fromUnit, toUnit) {
return value;
}
-function getCellDataType(entry) {
- if (['temperature', 'duration'].includes(entry)) {
- return 'property';
+function getStandardUnits(entry) {
+ switch (entry) {
+ case 'volume':
+ return volumeUnits;
+ case 'mass':
+ return massUnits;
+ case 'amount':
+ return amountUnits;
+ case 'temperature':
+ return temperatureUnits;
+ case 'duration':
+ return durationUnits;
+ case 'concentration':
+ return concentrationUnits;
+ default:
+ return [null];
}
- if (entry === 'equivalent') {
- return 'equivalent';
+}
+
+function getStandardValue(entry, material) {
+ switch (entry) {
+ case 'volume':
+ return material.amount_l ?? null;
+ case 'mass':
+ return material.amount_g ?? null;
+ case 'amount':
+ return material.amount_mol ?? null;
+ case 'equivalent':
+ return (material.reference ?? false) ? 1 : 0;
+ case 'temperature': {
+ const { value = null, unit = null } = material.gas_phase_data?.temperature ?? {};
+ return convertUnit(value, unit, getStandardUnits('temperature')[0]);
+ }
+ case 'concentration':
+ return material.gas_phase_data?.part_per_million ?? null;
+ case 'turnoverNumber':
+ return material.gas_phase_data?.turnover_number ?? null;
+ case 'turnoverFrequency':
+ return material.gas_phase_data?.turnover_frequency?.value ?? null;
+ default:
+ return null;
}
- if (['mass', 'volume', 'amount'].includes(entry)) {
- return 'material';
+}
+
+function getCellDataType(entry, gasType = 'off') {
+ switch (entry) {
+ case 'temperature':
+ case 'duration':
+ return gasType === 'off' ? 'property' : 'gas';
+ case 'equivalent':
+ return gasType === 'feedstock' ? 'feedstock' : 'equivalent';
+ case 'mass':
+ case 'volume':
+ case 'amount':
+ return gasType === 'feedstock' ? 'feedstock' : 'material';
+ case 'concentration':
+ case 'turnoverNumber':
+ case 'turnoverFrequency':
+ return 'gas';
+ case 'yield':
+ return 'yield';
+ default:
+ return null;
}
- return null;
}
function getVariationsRowName(reactionLabel, variationsRowId) {
@@ -79,20 +126,20 @@ function getSequentialId(variations) {
return (ids.length === 0) ? 1 : Math.max(...ids) + 1;
}
-function createVariationsRow(reaction, variations) {
+function createVariationsRow(reaction, variations, gasMode = false, vesselVolume = null) {
const reactionCopy = cloneDeep(reaction);
const { dispValue: durationValue = null, dispUnit: durationUnit = 'None' } = reactionCopy.durationDisplay ?? {};
const { userText: temperatureValue = null, valueUnit: temperatureUnit = 'None' } = reactionCopy.temperature ?? {};
- let row = {
+ const row = {
id: getSequentialId(variations),
properties: {
temperature: {
- value: convertUnit(temperatureValue, temperatureUnit, getStandardUnit('temperature')),
- unit: getStandardUnit('temperature')
+ value: convertUnit(temperatureValue, temperatureUnit, getStandardUnits('temperature')[0]),
+ unit: getStandardUnits('temperature')[0]
},
duration: {
- value: convertUnit(durationValue, durationUnit, getStandardUnit('duration')),
- unit: getStandardUnit('duration'),
+ value: convertUnit(durationValue, durationUnit, getStandardUnits('duration')[0]),
+ unit: getStandardUnits('duration')[0],
},
},
analyses: [],
@@ -100,7 +147,7 @@ function createVariationsRow(reaction, variations) {
};
Object.entries(materialTypes).forEach(([materialType, { reactionAttributeName }]) => {
row[materialType] = reactionCopy[reactionAttributeName].reduce((a, v) => (
- { ...a, [v.id]: getMaterialData(v) }), {});
+ { ...a, [v.id]: getMaterialData(v, materialType, gasMode, vesselVolume) }), {});
});
return updateVariationsRowOnReferenceMaterialChange(row, reactionCopy.has_polymers);
@@ -119,16 +166,18 @@ function updateVariationsRow(row, field, value, reactionHasPolymers) {
/*
Some attributes of a material need to be updated in response to changes in other attributes:
- attribute | needs to be updated in response to
- -----------|----------------------------------
- equivalent | own mass changes^, own amount changes^, reference material's mass changes~, reference material's amount changes~
- mass | own amount changes^, own equivalent changes^
- amount | own mass changes^, own equivalent changes^
- yield | own mass changes^, own amount changes^x, reference material's mass changes~, reference material's amount changes~
+ attribute | needs to be updated in response to change in
+ ------------------|---------------------------------------------
+ equivalent | mass^, amount^, reference material's mass~, reference material's amount~
+ mass | amount^, equivalent^, concentration^, temperature^
+ amount | mass^, equivalent^, concentration^, temperature^
+ yield | mass^, amount^x, concentration^, temperature^, reference material's mass~, reference material's amount~
+ turnoverNumber | concentration^, temperature^
+ turnoverFrequency | concentration^, temperature^, duration^
- ^: handled in corresponding cell parsers (changes within single material)
+ ^: handled in cell parsers (changes within single material)
~: handled here (row-wide changes across materials)
- x: not permitted according to business logic
+ ^x: not permitted according to business logic
*/
let updatedRow = cloneDeep(row);
set(updatedRow, field, value);
@@ -145,11 +194,15 @@ function updateColumnDefinitions(columnDefinitions, field, property, newValue) {
updatedColumnDefinitions.forEach((columnDefinition) => {
if (columnDefinition.groupId) {
// Column group.
- columnDefinition.children.forEach((child) => {
- if (child.field === field) {
- child[property] = newValue;
- }
- });
+ if (columnDefinition.groupId === field) {
+ columnDefinition[property] = newValue;
+ } else {
+ columnDefinition.children.forEach((child) => {
+ if (child.field === field) {
+ child[property] = newValue;
+ }
+ });
+ }
} else if (columnDefinition.field === field) {
// Single column.
columnDefinition[property] = newValue;
@@ -165,13 +218,15 @@ export {
amountUnits,
temperatureUnits,
durationUnits,
+ concentrationUnits,
+ getStandardUnits,
convertUnit,
- getStandardUnit,
materialTypes,
getVariationsRowName,
createVariationsRow,
copyVariationsRow,
updateVariationsRow,
updateColumnDefinitions,
- getCellDataType
+ getCellDataType,
+ getStandardValue,
};
diff --git a/app/packs/src/models/Reaction.js b/app/packs/src/models/Reaction.js
index 9390ed19b5..753e4d8a46 100644
--- a/app/packs/src/models/Reaction.js
+++ b/app/packs/src/models/Reaction.js
@@ -206,83 +206,7 @@ export default class Reaction extends Element {
set variations(variations) {
/*
- variations data structure (also see Entities::ReactionVariationEntity):
- [
- {
- "id":
,
- "notes": ,
- "properties": {
- "temperature": {"value": , "unit": },
- "duration": {"value": , "unit": }
- },
- "analyses": [, , ...],
- "startingMaterials": {
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- ...
- },
- "reactants": {
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- ...
- },
- "products": {
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- ...
- },
- "solvents": {
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- : {
- "mass": {"value": , "unit": },
- "amount": {"value": , "unit": },
- "volume": {"value": , "unit": },
- "aux": {...}
- },
- ...
- },
- },
- {
- "id": "",
- ...
- },
- ...
- ]
+ See Entities::ReactionVariationEntity for details on the data structure.
Units are to be treated as immutable. Units and corresponding values
are changed (not mutated in the present data-structure!) only for display or export
diff --git a/db/migrate/20241129093956_add_gas_materials_to_reaction_variations.rb b/db/migrate/20241129093956_add_gas_materials_to_reaction_variations.rb
new file mode 100644
index 0000000000..78496a56a7
--- /dev/null
+++ b/db/migrate/20241129093956_add_gas_materials_to_reaction_variations.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+SAMPLES_TYPES = %w[startingMaterials products reactants solvents].freeze
+
+def migrate_aux(material, material_type)
+ material['aux'].delete('yield')
+ material['aux'].delete('equivalent')
+
+ material['aux']['materialType'] ||= material_type
+ material['aux']['gasType'] ||= 'off'
+ material['aux']['vesselVolume'] ||= 0
+end
+
+def migrate_starting_material(material)
+ # Transfer `equivalent` from `aux` to `equivalent` entry.
+ equivalent = material['aux']['equivalent']
+ material['equivalent'] ||= { 'value' => equivalent, 'unit' => nil }
+
+ material.delete('volume') if material['aux']['gasType'] != 'feedstock'
+end
+
+def migrate_solvent(material)
+ material.delete('mass')
+ material.delete('amount')
+end
+
+def migrate_product(material)
+ # Transfer `yield` from `aux` to `yield` entry.
+ percent_yield = material['aux']['yield']
+ material['yield'] ||= { 'value' => percent_yield, 'unit' => nil }
+
+ material.delete('volume')
+end
+
+class AddGasMaterialsToReactionVariations < ActiveRecord::Migration[6.1]
+ def up
+ # Prior to this migration all materials have a `mass`, `amount`, and `volume` entry, irrespective of material type.
+ Reaction.where.not('variations = ?', '[]').find_each do |reaction|
+ variations = reaction.variations
+ variations.each do |variation|
+ SAMPLES_TYPES.each do |key|
+ variation[key].each_value do |material|
+ case key
+ when 'startingMaterials', 'reactants'
+ migrate_starting_material(material)
+ when 'solvents'
+ migrate_solvent(material)
+ when 'products'
+ migrate_product(material)
+ end
+ migrate_aux(material, key)
+ end
+ end
+ end
+ reaction.update_column(:variations, variations)
+ end
+ end
+
+ def down
+ # After this migration all materials have a `mass`, `amount`, and `volume` entry, irrespective of material type.
+ Reaction.where.not('variations = ?', '[]').find_each do |reaction|
+ variations = reaction.variations
+ variations.each do |variation|
+ SAMPLES_TYPES.each do |key|
+ variation[key].each_value do |material|
+ material['aux'].delete('materialType')
+ material['aux'].delete('gasType')
+ material['aux'].delete('vesselVolume')
+
+ percent_yield = material['yield']&.[]('value')
+ material['aux']['yield'] ||= percent_yield
+ material.delete('yield')
+
+ equivalent = material['equivalent']&.[]('value')
+ material['aux']['equivalent'] ||= equivalent
+ material.delete('equivalent')
+
+ material.delete('duration')
+ material.delete('temperature')
+ material.delete('concentration')
+ material.delete('turnoverNumber')
+ material.delete('turnoverFrequency')
+
+ material['mass'] ||= { 'value' => 0, 'unit' => 'g' }
+ material['amount'] ||= { 'value' => 0, 'unit' => 'mol' }
+ material['volume'] ||= { 'value' => 0, 'unit' => 'l' }
+ end
+ end
+ end
+ reaction.update_column(:variations, variations)
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 12ab0a0f71..b2a1c523d6 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: 2024_07_11_120833) do
+ActiveRecord::Schema.define(version: 2024_11_29_093956) do
# These are extensions that must be enabled in order to support this database
enable_extension "hstore"
diff --git a/lib/reporter/docx/detail_reaction.rb b/lib/reporter/docx/detail_reaction.rb
index d060908651..ca1b309f40 100644
--- a/lib/reporter/docx/detail_reaction.rb
+++ b/lib/reporter/docx/detail_reaction.rb
@@ -60,23 +60,25 @@ def variations
'startingMaterials' => variation_materials(var, :startingMaterials),
'reactants' => variation_materials(var, :reactants),
'solvents' => variation_materials(var, :solvents),
- 'products' => variation_products(var),
+ 'products' => variation_materials(var, :products),
'notes' => var[:notes],
}
end
end
def variation_materials(variation, type)
- variation[type].map do |_, v|
- "#{v[:aux][:sumFormula]}:\n#{v[:value]} #{v[:unit]} " \
- "; #{v[:aux].fetch(:equivalent, 'n/a')} Equiv " +
- (v[:aux][:isReference] ? '; Ref' : '')
- end
- end
+ variation[type].map do |_, vi|
+ result = "#{vi[:aux][:sumFormula]}:\n"
+
+ meta_data = [vi[:aux][:isReference] ? 'Ref' : '', vi[:aux][:gasType] == 'off' ? '' : vi[:aux][:gasType]]
+ meta_data = meta_data.reject(&:empty?).join(', ')
+ result += "(#{meta_data})\n" if meta_data.present?
+
+ result + vi.map do |k, vj|
+ next if k == :aux
- def variation_products(variation)
- variation[:products].map do |_, v|
- "#{v[:aux][:sumFormula]}:\n#{v[:value]} #{v[:unit]}; (#{v[:aux][:yield]} % yield)"
+ "#{k.to_s.gsub(/([A-Z])/, ' \1').downcase.strip}: #{vj[:value]} #{vj[:unit]};\n"
+ end.join
end
end
diff --git a/spec/api/entities/reaction_entity_spec.rb b/spec/api/entities/reaction_entity_spec.rb
index 7fa4226336..b5979c688d 100644
--- a/spec/api/entities/reaction_entity_spec.rb
+++ b/spec/api/entities/reaction_entity_spec.rb
@@ -27,158 +27,182 @@
variations: [{ id: '1',
analyses: [1, 2],
notes: '',
- products: { '47': { aux: { yield: 0,
- purity: 0.29,
+ products: { '47': { aux: { purity: 0.29,
loading: nil,
molarity: 0,
sumFormula: 'C21H18N2O2',
coefficient: 1,
isReference: false,
molecularWeight: 330.37982,
- equivalent: nil },
+ gasType: 'gas',
+ materialType: 'products',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } },
- '48': { aux: { yield: 100,
- purity: 0.12,
+ amount: { unit: 'mol', value: nil },
+ yield: { unit: nil, value: nil },
+ duration: { unit: 'Second(s)', value: 42 },
+ temperature: { unit: 'K', value: 42 },
+ concentration: { unit: nil, value: 42 },
+ turnoverNumber: { unit: nil, value: 42 },
+ turnoverFrequency: { unit: nil, value: 42 } },
+ '48': { aux: { purity: 0.12,
loading: nil,
molarity: 0,
sumFormula: 'C31H30O4',
coefficient: 1,
isReference: false,
molecularWeight: 466.56750000000005,
- equivalent: nil },
+ gasType: 'off',
+ materialType: 'products',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
+ yield: { unit: nil, value: nil },
amount: { unit: 'mol', value: nil } } },
- reactants: { '45': { aux: { yield: nil,
- purity: 0.78,
+ reactants: { '45': { aux: { purity: 0.78,
loading: nil,
molarity: 0,
sumFormula: 'C10H17NO2S',
coefficient: 1,
isReference: false,
molecularWeight: 215.31247999999997,
- equivalent: nil },
+ gasType: 'catalyst',
+ materialType: 'reactants',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } },
- '46': { aux: { yield: nil,
- purity: 0.84,
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil } },
+ '46': { aux: { purity: 0.84,
loading: nil,
molarity: 0,
sumFormula: 'C10H16O4S',
coefficient: 1,
isReference: false,
molecularWeight: 232.29664,
- equivalent: nil },
+ gasType: 'off',
+ materialType: 'reactants',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } } },
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil } } },
properties: { duration: { foo: {}, unit: 'Hour(s)', value: '19' },
temperature: { unit: 'C', value: '1' } },
- startingMaterials: { '43': { aux: { yield: nil,
- purity: 0.25,
+ startingMaterials: { '43': { aux: { purity: 0.25,
loading: nil,
molarity: 0,
sumFormula: 'C10H10BF4IN2',
coefficient: 1,
isReference: true,
molecularWeight: 371.9088828000001,
- equivalent: nil },
+ gasType: 'feedstock',
+ materialType: 'startingMaterials',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } },
- '44': { aux: { yield: nil,
- purity: 0.4,
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil },
+ volume: { unit: 'ml', value: 42 } },
+ '44': { aux: { purity: 0.4,
loading: nil,
molarity: 0,
sumFormula: 'C32H72Cr2N2O7',
coefficient: 1,
isReference: false,
molecularWeight: 700.9154799999998,
- equivalent: nil },
+ gasType: 'off',
+ materialType: 'startingMaterials',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } } },
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil } } },
solvents: {} },
{ id: '2',
analyses: [],
notes: '',
- products: { '47': { aux: { yield: 0,
- purity: 0.29,
+ products: { '47': { aux: { purity: 0.29,
loading: nil,
molarity: 0,
sumFormula: 'C21H18N2O2',
coefficient: 1,
isReference: false,
molecularWeight: 330.37982,
- equivalent: nil },
+ gasType: 'gas',
+ materialType: 'products',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } },
- '48': { aux: { yield: 2,
- purity: 0.12,
+ amount: { unit: 'mol', value: nil },
+ yield: { unit: nil, value: nil },
+ duration: { unit: 'Second(s)', value: 42 },
+ temperature: { unit: 'K', value: 42 },
+ concentration: { unit: nil, value: 42 },
+ turnoverNumber: { unit: nil, value: 42 },
+ turnoverFrequency: { unit: nil, value: 42 } },
+ '48': { aux: { purity: 0.12,
loading: nil,
molarity: 0,
sumFormula: 'C31H30O4',
coefficient: 1,
isReference: false,
molecularWeight: 466.56750000000005,
- equivalent: nil },
+ gasType: 'off',
+ materialType: 'products',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
+ yield: { unit: nil, value: nil },
amount: { unit: 'mol', value: nil } } },
- reactants: { '45': { aux: { yield: nil,
- purity: 0.78,
+ reactants: { '45': { aux: { purity: 0.78,
loading: nil,
molarity: 0,
sumFormula: 'C10H17NO2S',
coefficient: 1,
isReference: false,
molecularWeight: 215.31247999999997,
- equivalent: nil },
+ gasType: 'catalyst',
+ materialType: 'reactants',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } },
- '46': { aux: { yield: nil,
- purity: 0.84,
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil } },
+ '46': { aux: { purity: 0.84,
loading: nil,
molarity: 0,
sumFormula: 'C10H16O4S',
coefficient: 1,
isReference: false,
molecularWeight: 232.29664,
- equivalent: nil },
+ gasType: 'off',
+ materialType: 'reactants',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } } },
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil } } },
properties: { duration: { unit: 'Hour(s)', value: '15' },
temperature: { unit: 'C', value: '2' } },
- startingMaterials: { '43': { aux: { yield: nil,
- purity: 0.25,
+ startingMaterials: { '43': { aux: { purity: 0.25,
loading: nil,
molarity: 0,
sumFormula: 'C10H10BF4IN2',
coefficient: 1,
isReference: true,
molecularWeight: 371.9088828000001,
- equivalent: nil },
+ gasType: 'feedstock',
+ materialType: 'startingMaterials',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } },
- '44': { aux: { yield: nil,
- purity: 0.4,
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil },
+ volume: { unit: 'ml', value: 42 } },
+ '44': { aux: { purity: 0.4,
loading: nil,
molarity: 0,
sumFormula: 'C32H72Cr2N2O7',
coefficient: 1,
isReference: false,
molecularWeight: 700.9154799999998,
- equivalent: nil },
+ gasType: 'off',
+ materialType: 'startingMaterials',
+ vesselVolume: 42 },
mass: { unit: 'g', value: nil },
- volume: { unit: 'l', value: nil },
- amount: { unit: 'mol', value: nil } } },
+ amount: { unit: 'mol', value: nil },
+ equivalent: { unit: nil, value: nil } } },
solvents: {} }],
)
end
diff --git a/spec/javascripts/helper/reactionVariationsHelpers.js b/spec/javascripts/helper/reactionVariationsHelpers.js
index a6e5f2b9bc..25ba0a01f9 100644
--- a/spec/javascripts/helper/reactionVariationsHelpers.js
+++ b/spec/javascripts/helper/reactionVariationsHelpers.js
@@ -21,6 +21,30 @@ async function setUpReaction() {
return reaction;
}
+async function setUpGaseousReaction() {
+ const reaction = await ReactionFactory.build('ReactionFactory.water+water=>water+water');
+ reaction.starting_materials[0].reference = true;
+ reaction.starting_materials[0].gas_type = 'catalyst';
+ reaction.reactants = [await setUpMaterial()];
+ reaction.reactants[0].gas_type = 'feedstock';
+ reaction.products[0].gas_type = 'gas';
+ reaction.products[0].gas_phase_data = {
+ time: { unit: 'h', value: 1 },
+ temperature: { unit: 'K', value: 1 },
+ turnover_number: 1,
+ part_per_million: 1,
+ turnover_frequency: { unit: 'TON/h', value: 1 }
+ };
+
+ const variations = [];
+ for (let id = 0; id < 3; id++) {
+ variations.push(createVariationsRow(reaction, variations, true, 10));
+ }
+ reaction.variations = variations;
+
+ return reaction;
+}
+
function getColumnGroupChild(columnDefinitions, groupID, fieldID) {
const columnGroup = columnDefinitions.find((group) => group.groupId === groupID);
const columnDefinition = columnGroup.children.find((child) => child.field === fieldID);
@@ -39,5 +63,5 @@ function getColumnDefinitionsMaterialIDs(columnDefinitions, materialType) {
}
export {
- setUpMaterial, setUpReaction, getColumnGroupChild, getColumnDefinitionsMaterialIDs
+ setUpMaterial, setUpReaction, setUpGaseousReaction, getColumnGroupChild, getColumnDefinitionsMaterialIDs
};
diff --git a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents.spec.js b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents.spec.js
deleted file mode 100644
index 3f490136c2..0000000000
--- a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents.spec.js
+++ /dev/null
@@ -1,108 +0,0 @@
-import expect from 'expect';
-import {
- EquivalentFormatter, EquivalentParser, PropertyFormatter, PropertyParser, MaterialFormatter, MaterialParser
-} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents';
-import { setUpReaction } from 'helper/reactionVariationsHelpers';
-
-describe('ReactionVariationsCellComponents', async () => {
- describe('FormatterComponents', () => {
- it('EquivalentFormatter returns number string with correct precision', () => {
- const cellData = { aux: { equivalent: 1.2345 } };
-
- expect(EquivalentFormatter({ value: cellData })).toEqual('1.234');
- });
- it('PropertyFormatter returns number string with correct precision', () => {
- const cellData = { value: 1.2345, unit: 'Second(s)' };
- const colDef = { currentEntryWithDisplayUnit: { displayUnit: 'Minute(s)' } };
-
- expect(PropertyFormatter({ value: cellData, colDef })).toEqual('0.02057');
- });
- it('MaterialFormatter returns number string with correct precision', () => {
- const cellData = { amount: { value: 1.2345, unit: 'mol' } };
- const colDef = { currentEntryWithDisplayUnit: { entry: 'amount', displayUnit: 'mmol' } };
-
- expect(MaterialFormatter({ value: cellData, colDef })).toEqual('1235');
- });
- });
- describe('EquivalentParser', async () => {
- let variationsRow;
- let cellData;
- beforeEach(async () => {
- const reaction = await setUpReaction();
- variationsRow = reaction.variations[0];
- cellData = Object.values(variationsRow.reactants)[0];
- });
- it('rejects negative value', () => {
- const newValue = '-1';
- const updatedCellData = EquivalentParser({ data: variationsRow, oldValue: cellData, newValue });
-
- expect(updatedCellData.aux.equivalent).toEqual(0);
- });
- it('updates mass and amount', () => {
- const newValue = '2';
- const updatedCellData = EquivalentParser({ data: variationsRow, oldValue: cellData, newValue });
-
- expect(updatedCellData.mass.value).toBeCloseTo(cellData.mass.value * 2, 0.01);
- expect(updatedCellData.amount.value).toBeCloseTo(cellData.amount.value * 2, 0.01);
- });
- });
- describe('PropertyParser', async () => {
- it('rejects negative value for duration', () => {
- const cellData = { value: 120, unit: 'Second(s)' };
- const colDef = { currentEntryWithDisplayUnit: { entry: 'duration', displayUnit: 'Minute(s)' } };
- const newValue = '-1';
- const updatedCellData = PropertyParser({ oldValue: cellData, newValue, colDef });
-
- expect(updatedCellData.value).toEqual(0);
- });
- it('accepts negative value for temperature', () => {
- const cellData = { value: 120, unit: '°C' };
- const colDef = { currentEntryWithDisplayUnit: { entry: 'temperature', displayUnit: 'K' } };
- const newValue = '-1';
- const updatedCellData = PropertyParser({ oldValue: cellData, newValue, colDef });
-
- expect(updatedCellData.value).toEqual(-274.15);
- });
- });
- describe('MaterialParser', async () => {
- let variationsRow;
- let cellData;
- let context;
- beforeEach(async () => {
- const reaction = await setUpReaction();
- variationsRow = reaction.variations[0];
- cellData = Object.values(variationsRow.reactants)[0];
- context = { reactionHasPolymers: false };
- });
- it('rejects negative value', () => {
- const colDef = { field: 'reactants.42', currentEntryWithDisplayUnit: { entry: 'amount', displayUnit: 'mmol' } };
- const updatedCellData = MaterialParser({
- data: variationsRow, oldValue: cellData, newValue: '-1', colDef, context
- });
-
- expect(updatedCellData.amount.value).toEqual(0);
- });
- it('adapts mass when updating amount', () => {
- const colDef = { field: 'reactants.42', currentEntryWithDisplayUnit: { entry: 'amount', displayUnit: 'mmol' } };
-
- expect(cellData.mass.value).toBe(100);
-
- const updatedCellData = MaterialParser({
- data: variationsRow, oldValue: cellData, newValue: '42', colDef, context
- });
-
- expect(updatedCellData.mass.value).toBeCloseTo(0.75, 0.1);
- });
- it('adapts amount when updating mass', () => {
- const colDef = { field: 'reactants.42', currentEntryWithDisplayUnit: { entry: 'mass', displayUnit: 'g' } };
-
- expect(cellData.amount.value).toBeCloseTo(5.5, 0.1);
-
- const updatedCellData = MaterialParser({
- data: variationsRow, oldValue: cellData, newValue: '42', colDef, context
- });
-
- expect(updatedCellData.amount.value).toBeCloseTo(2.33, 0.1);
- });
- });
-});
diff --git a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.spec.js b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.spec.js
new file mode 100644
index 0000000000..7789512650
--- /dev/null
+++ b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.spec.js
@@ -0,0 +1,236 @@
+import expect from 'expect';
+import {
+ EquivalentParser, PropertyFormatter, PropertyParser, MaterialFormatter, MaterialParser, FeedstockParser, GasParser
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents';
+import { setUpReaction, setUpGaseousReaction } from 'helper/reactionVariationsHelpers';
+
+describe('ReactionVariationsComponents', async () => {
+ describe('FormatterComponents', () => {
+ it('PropertyFormatter returns number string with correct precision', () => {
+ const cellData = { value: 1.2345, unit: 'Second(s)' };
+ const colDef = { entryDefs: { displayUnit: 'Minute(s)' } };
+
+ expect(PropertyFormatter({ value: cellData, colDef })).toEqual('0.02057');
+ });
+ it('MaterialFormatter returns number string with correct precision', () => {
+ const cellData = { amount: { value: 1.2345, unit: 'mol' } };
+ const colDef = { entryDefs: { currentEntry: 'amount', displayUnit: 'mmol' } };
+
+ expect(MaterialFormatter({ value: cellData, colDef })).toEqual('1235');
+ });
+ });
+ describe('EquivalentParser', async () => {
+ let variationsRow;
+ let cellData;
+ beforeEach(async () => {
+ const reaction = await setUpReaction();
+ variationsRow = reaction.variations[0];
+ cellData = Object.values(variationsRow.reactants)[0];
+ });
+ it('rejects negative value', () => {
+ const newValue = '-1';
+ const updatedCellData = EquivalentParser({ data: variationsRow, oldValue: cellData, newValue });
+
+ expect(updatedCellData.equivalent.value).toEqual(0);
+ });
+ it('updates mass and amount', () => {
+ const newValue = '2';
+ const updatedCellData = EquivalentParser({ data: variationsRow, oldValue: cellData, newValue });
+
+ expect(updatedCellData.mass.value).toBeCloseTo(cellData.mass.value * 2, 0.01);
+ expect(updatedCellData.amount.value).toBeCloseTo(cellData.amount.value * 2, 0.01);
+ });
+ });
+ describe('PropertyParser', async () => {
+ it('rejects negative value for duration', () => {
+ const cellData = { value: 120, unit: 'Second(s)' };
+ const colDef = { entryDefs: { currentEntry: 'duration', displayUnit: 'Minute(s)' } };
+ const newValue = '-1';
+ const updatedCellData = PropertyParser({ oldValue: cellData, newValue, colDef });
+
+ expect(updatedCellData.value).toEqual(0);
+ });
+ it('accepts negative value for temperature', () => {
+ const cellData = { value: 120, unit: '°C' };
+ const colDef = { entryDefs: { currentEntry: 'temperature', displayUnit: 'K' } };
+ const newValue = '-1';
+ const updatedCellData = PropertyParser({ oldValue: cellData, newValue, colDef });
+
+ expect(updatedCellData.value).toEqual(-273.15);
+ });
+ });
+ describe('MaterialParser', async () => {
+ let variationsRow;
+ let cellData;
+ let context;
+ beforeEach(async () => {
+ const reaction = await setUpReaction();
+ variationsRow = reaction.variations[0];
+ cellData = Object.values(variationsRow.reactants)[0];
+ context = { reactionHasPolymers: false };
+ });
+ it('rejects negative value', () => {
+ const colDef = { field: 'reactants.42', entryDefs: { currentEntry: 'amount', displayUnit: 'mmol' } };
+ const updatedCellData = MaterialParser({
+ data: variationsRow, oldValue: cellData, newValue: '-1', colDef, context
+ });
+
+ expect(updatedCellData.amount.value).toEqual(0);
+ });
+ it('adapts mass when updating amount', () => {
+ const colDef = { field: 'reactants.42', entryDefs: { currentEntry: 'amount', displayUnit: 'mmol' } };
+
+ expect(cellData.mass.value).toBe(100);
+
+ const updatedCellData = MaterialParser({
+ data: variationsRow, oldValue: cellData, newValue: '42', colDef, context
+ });
+
+ expect(updatedCellData.mass.value).toBeCloseTo(0.75, 0.1);
+ });
+ it('adapts amount when updating mass', () => {
+ const colDef = { field: 'reactants.42', entryDefs: { currentEntry: 'mass', displayUnit: 'g' } };
+
+ expect(cellData.amount.value).toBeCloseTo(5.5, 0.1);
+
+ const updatedCellData = MaterialParser({
+ data: variationsRow, oldValue: cellData, newValue: '42', colDef, context
+ });
+
+ expect(updatedCellData.amount.value).toBeCloseTo(2.33, 0.1);
+ });
+ it("adapts non-reference materials' equivalent when updating mass", async () => {
+ const colDef = { field: 'reactants.42', entryDefs: { currentEntry: 'mass', displayUnit: 'g' } };
+
+ const updatedCellData = MaterialParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.mass.value * 2}`, colDef, context
+ });
+
+ expect(updatedCellData.equivalent.value).toBe(cellData.equivalent.value * 2);
+ });
+ it("adapts non-reference materials' yield when updating mass", async () => {
+ cellData = Object.values(variationsRow.products)[0];
+ const colDef = { field: 'products.42', entryDefs: { currentEntry: 'mass', displayUnit: 'g' } };
+
+ const updatedCellData = MaterialParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.mass.value * 0.1}`, colDef, context
+ });
+
+ expect(updatedCellData.yield.value).toBeLessThan(cellData.yield.value);
+ });
+ });
+ describe('FeedstockParser', async () => {
+ let variationsRow;
+ let cellData;
+ let context;
+ beforeEach(async () => {
+ const reaction = await setUpGaseousReaction();
+ variationsRow = reaction.variations[0];
+ cellData = Object.values(variationsRow.reactants)[0];
+ context = { reactionHasPolymers: false };
+ });
+ it('rejects negative value', () => {
+ const colDef = { field: 'reactants.42', entryDefs: { currentEntry: 'equivalent', displayUnit: null } };
+ const updatedCellData = FeedstockParser({
+ data: variationsRow, oldValue: cellData, newValue: '-1', colDef, context
+ });
+
+ expect(updatedCellData.equivalent.value).toEqual(0);
+ });
+ it('adapts nothing when updating equivalent', () => {
+ const colDef = { field: 'reactant.42', entryDefs: { currentEntry: 'equivalent', displayUnit: null } };
+
+ const updatedCellData = FeedstockParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.equivalent.value * 2}`, colDef, context
+ });
+
+ expect(updatedCellData.equivalent.value).toBe(cellData.equivalent.value * 2);
+ expect(updatedCellData.mass.value).toBe(cellData.mass.value);
+ expect(updatedCellData.amount.value).toBe(cellData.amount.value);
+ expect(updatedCellData.volume.value).toBe(cellData.volume.value);
+ });
+ it('adapts other entries when updating volume', () => {
+ const colDef = { field: 'reactant.42', entryDefs: { currentEntry: 'volume', displayUnit: 'l' } };
+
+ const updatedCellData = FeedstockParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.volume.value * 2}`, colDef, context
+ });
+
+ expect(updatedCellData.volume.value).toBe(cellData.volume.value * 2);
+ expect(updatedCellData.mass.value).toBeGreaterThan(cellData.mass.value);
+ expect(updatedCellData.amount.value).toBeGreaterThan(cellData.amount.value);
+ expect(updatedCellData.equivalent.value).toBeGreaterThan(cellData.equivalent.value);
+ });
+ it('adapts other entries when updating amount', () => {
+ const colDef = { field: 'reactant.42', entryDefs: { currentEntry: 'amount', displayUnit: 'mol' } };
+
+ const updatedCellData = FeedstockParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.amount.value * 2}`, colDef, context
+ });
+
+ expect(updatedCellData.amount.value).toBe(cellData.amount.value * 2);
+ expect(updatedCellData.mass.value).toBeGreaterThan(cellData.mass.value);
+ expect(updatedCellData.volume.value).toBeGreaterThan(cellData.volume.value);
+ expect(updatedCellData.equivalent.value).toBeGreaterThan(cellData.equivalent.value);
+ });
+ });
+ describe('GasParser', async () => {
+ let variationsRow;
+ let cellData;
+ let context;
+ beforeEach(async () => {
+ const reaction = await setUpGaseousReaction();
+ variationsRow = reaction.variations[0];
+ cellData = Object.values(variationsRow.products)[0];
+ context = { reactionHasPolymers: false };
+ });
+ it('rejects negative value', () => {
+ const colDef = { field: 'products.42', entryDefs: { currentEntry: 'duration', displayUnit: 'Hour(s)' } };
+ const updatedCellData = GasParser({
+ data: variationsRow, oldValue: cellData, newValue: '-1', colDef, context
+ });
+
+ expect(updatedCellData.duration.value).toEqual(0);
+ });
+ it('adapts only turnover frequency when updating duration', () => {
+ const colDef = { field: 'products.42', entryDefs: { currentEntry: 'duration', displayUnit: 'Hour(s)' } };
+
+ const updatedCellData = GasParser({
+ data: variationsRow, oldValue: cellData, newValue: '2', colDef, context
+ });
+
+ expect(updatedCellData.mass.value).toBe(cellData.mass.value);
+ expect(updatedCellData.amount.value).toBe(cellData.amount.value);
+ expect(updatedCellData.yield.value).toBe(cellData.yield.value);
+ expect(updatedCellData.turnoverNumber.value).toBe(cellData.turnoverNumber.value);
+
+ expect(updatedCellData.turnoverFrequency.value).toBeLessThan(cellData.turnoverFrequency.value);
+ });
+ it('adapts other entries when updating concentration', () => {
+ const colDef = { field: 'products.42', entryDefs: { currentEntry: 'concentration', displayUnit: 'ppm' } };
+
+ const updatedCellData = GasParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.concentration.value * 2}`, colDef, context
+ });
+
+ expect(updatedCellData.mass.value).not.toBe(cellData.mass.value);
+ expect(updatedCellData.amount.value).not.toBe(cellData.amount.value);
+ expect(updatedCellData.yield.value).not.toBe(cellData.yield.value);
+ expect(updatedCellData.turnoverNumber.value).not.toBe(cellData.turnoverNumber.value);
+ expect(updatedCellData.turnoverFrequency.value).not.toBe(cellData.turnoverFrequency.value);
+ });
+ it('adapts other entries when updating temperature', () => {
+ const colDef = { field: 'products.42', entryDefs: { currentEntry: 'temperature', displayUnit: 'K' } };
+
+ const updatedCellData = GasParser({
+ data: variationsRow, oldValue: cellData, newValue: `${cellData.temperature.value / 2}`, colDef, context
+ });
+
+ expect(updatedCellData.mass.value).not.toBe(cellData.mass.value);
+ expect(updatedCellData.amount.value).not.toBe(cellData.amount.value);
+ expect(updatedCellData.yield.value).not.toBe(cellData.yield.value);
+ expect(updatedCellData.turnoverNumber.value).not.toBe(cellData.turnoverNumber.value);
+ expect(updatedCellData.turnoverFrequency.value).not.toBe(cellData.turnoverFrequency.value);
+ });
+ });
+});
diff --git a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.spec.js b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.spec.js
index 474174f6c3..77e80e41b3 100644
--- a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.spec.js
+++ b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.spec.js
@@ -2,13 +2,18 @@ import expect from 'expect';
import {
getReactionMaterials, updateVariationsRowOnReferenceMaterialChange,
removeObsoleteMaterialsFromVariations, addMissingMaterialsToVariations,
- updateNonReferenceMaterialOnMassChange, updateColumnDefinitionsMaterials,
- getMaterialColumnGroupChild, getReactionMaterialsIDs
+ updateColumnDefinitionsMaterials,
+ getMaterialColumnGroupChild, getReactionMaterialsIDs, updateColumnDefinitionsMaterialTypes,
+ getReactionMaterialsGasTypes, updateVariationsGasTypes, cellIsEditable
} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials';
import {
EquivalentParser
-} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsCellComponents';
-import { setUpMaterial, setUpReaction, getColumnDefinitionsMaterialIDs } from 'helper/reactionVariationsHelpers';
+} from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents';
+import {
+ setUpMaterial, setUpReaction, setUpGaseousReaction, getColumnDefinitionsMaterialIDs, getColumnGroupChild
+} from 'helper/reactionVariationsHelpers';
+import { materialTypes } from 'src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils';
+import { cloneDeep } from 'lodash';
describe('ReactionVariationsMaterials', () => {
it('removes obsolete materials', async () => {
@@ -38,7 +43,7 @@ describe('ReactionVariationsMaterials', () => {
reaction.starting_materials.push(material);
const updatedStartingMaterialIDs = reaction.starting_materials.map((startingMaterial) => startingMaterial.id);
const currentMaterials = getReactionMaterials(reaction);
- const updatedVariations = addMissingMaterialsToVariations(reaction.variations, currentMaterials);
+ const updatedVariations = addMissingMaterialsToVariations(reaction.variations, currentMaterials, false);
updatedVariations
.forEach((variation) => {
expect(Object.keys(variation.startingMaterials)).toEqual(updatedStartingMaterialIDs);
@@ -47,49 +52,25 @@ describe('ReactionVariationsMaterials', () => {
it('updates yield when product mass changes', async () => {
const reaction = await setUpReaction();
const productID = reaction.products[0].id;
- expect(reaction.variations[0].products[productID].aux.yield).toBe(100);
+ expect(reaction.variations[0].products[productID].yield.value).toBe(100);
reaction.variations[0].products[productID].mass.value = 2;
const updatedVariationsRow = updateVariationsRowOnReferenceMaterialChange(
reaction.variations[0],
reaction.hasPolymers()
);
- expect(updatedVariationsRow.products[productID].aux.yield).toBe(5);
+ expect(updatedVariationsRow.products[productID].yield.value).toBe(5);
});
it("updates non-reference materials' equivalents when reference material's mass changes", async () => {
const reaction = await setUpReaction();
const reactantID = reaction.reactants[0].id;
- expect(reaction.variations[0].reactants[reactantID].aux.equivalent).toBe(1);
+ expect(reaction.variations[0].reactants[reactantID].equivalent.value).toBe(1);
Object.values(reaction.variations[0].startingMaterials).forEach((material) => {
if (material.aux.isReference) {
material.mass.value = 2;
}
});
const updatedVariationsRow = updateVariationsRowOnReferenceMaterialChange(reaction.variations[0]);
- expect(updatedVariationsRow.reactants[reactantID].aux.equivalent).toBeCloseTo(50, 0.01);
- });
- it("updates non-reference materials' equivalents when own mass changes", async () => {
- const reaction = await setUpReaction();
- const reactant = Object.values(reaction.variations[0].reactants)[0];
- reactant.mass.value *= 0.42;
- const updatedReactant = updateNonReferenceMaterialOnMassChange(
- reaction.variations[0],
- reactant,
- 'reactants',
- false
- );
- expect(reactant.aux.equivalent).toBeGreaterThan(updatedReactant.aux.equivalent);
- });
- it("updates non-reference materials' yields when own mass changes", async () => {
- const reaction = await setUpReaction();
- const product = Object.values(reaction.variations[0].products)[0];
- product.mass.value *= 0.042;
- const updatedProduct = updateNonReferenceMaterialOnMassChange(
- reaction.variations[0],
- product,
- 'products',
- true
- );
- expect(product.aux.yield).toBeGreaterThan(updatedProduct.aux.yield);
+ expect(updatedVariationsRow.reactants[reactantID].equivalent.value).toBeCloseTo(50, 0.01);
});
it("updates materials' mass when equivalent changes", async () => {
const reaction = await setUpReaction();
@@ -98,7 +79,7 @@ describe('ReactionVariationsMaterials', () => {
const updatedReactant = EquivalentParser({
data: variationsRow,
oldValue: reactant,
- newValue: Number(reactant.aux.equivalent * 0.42).toString()
+ newValue: Number(reactant.equivalent.value * 0.42).toString()
});
expect(reactant.mass.value).toBeGreaterThan(updatedReactant.mass.value);
expect(EquivalentParser({
@@ -112,7 +93,7 @@ describe('ReactionVariationsMaterials', () => {
const reactionMaterials = getReactionMaterials(reaction);
const columnDefinitions = Object.entries(reactionMaterials).map(([materialType, materials]) => ({
groupId: materialType,
- children: materials.map((material) => getMaterialColumnGroupChild(material, materialType, null))
+ children: materials.map((material) => getMaterialColumnGroupChild(material, materialType, null, false))
}));
const startingMaterialIDs = reactionMaterials.startingMaterials.map((material) => material.id);
@@ -120,7 +101,7 @@ describe('ReactionVariationsMaterials', () => {
reactionMaterials.startingMaterials.pop();
const updatedStartingMaterialIDs = reactionMaterials.startingMaterials.map((material) => material.id);
- const updatedColumnDefinitions = updateColumnDefinitionsMaterials(columnDefinitions, reactionMaterials, null);
+ const updatedColumnDefinitions = updateColumnDefinitionsMaterials(columnDefinitions, reactionMaterials, null, false);
expect(getColumnDefinitionsMaterialIDs(
updatedColumnDefinitions,
'startingMaterials'
@@ -133,4 +114,108 @@ describe('ReactionVariationsMaterials', () => {
expect(Array.isArray(reactionMaterialsIDs)).toBe(true);
expect(new Set(reactionMaterialsIDs).size).toBe(5);
});
+ it('retrieves reaction material gas types', async () => {
+ const reaction = await setUpGaseousReaction();
+ const reactionMaterials = getReactionMaterials(reaction);
+ const reactionMaterialsGasTypes = getReactionMaterialsGasTypes(reactionMaterials);
+ expect(reactionMaterialsGasTypes).toEqual(['catalyst', 'off', 'feedstock', 'gas', 'off']);
+ });
+ it("updates materials' gas type", async () => {
+ const reaction = await setUpGaseousReaction();
+ const currentMaterials = getReactionMaterials(reaction);
+
+ const updatedMaterials = cloneDeep(currentMaterials);
+ updatedMaterials.startingMaterials[0].gas_type = 'feedstock';
+ updatedMaterials.reactants[0].gas_type = 'catalyst';
+ updatedMaterials.products[0].gas_type = 'off';
+
+ const updatedVariations = updateVariationsGasTypes(reaction.variations, updatedMaterials, false);
+
+ const variationsRow = reaction.variations[0];
+ const updatedVariationsRow = updatedVariations[0];
+
+ expect(variationsRow.startingMaterials[Object.keys(variationsRow.startingMaterials)[0]].aux.gasType).not.toBe(
+ updatedVariationsRow.startingMaterials[Object.keys(updatedVariationsRow.startingMaterials)[0]].aux.gasType
+ );
+ expect(variationsRow.reactants[Object.keys(variationsRow.reactants)[0]].aux.gasType).not.toBe(
+ updatedVariationsRow.reactants[Object.keys(updatedVariationsRow.reactants)[0]].aux.gasType
+ );
+ expect(variationsRow.products[Object.keys(variationsRow.products)[0]].aux.gasType).not.toBe(
+ updatedVariationsRow.products[Object.keys(updatedVariationsRow.products)[0]].aux.gasType
+ );
+ });
+ it('updates column definitions of gaseous materials', async () => {
+ const reaction = await setUpReaction();
+
+ const reactionMaterials = getReactionMaterials(reaction);
+ const columnDefinitions = Object.entries(reactionMaterials).map(([materialType, materials]) => ({
+ groupId: materialType,
+ children: materials.map((material) => getMaterialColumnGroupChild(material, materialType, null, false))
+ }));
+
+ Object.keys(materialTypes).forEach((materialType) => {
+ reactionMaterials[materialType].forEach((material) => {
+ switch (materialType) {
+ case 'startingMaterials':
+ material.gas_type = 'catalyst';
+ break;
+ case 'reactants':
+ material.gas_type = 'feedstock';
+ break;
+ case 'products':
+ material.gas_type = 'gas';
+ break;
+ default:
+ break;
+ }
+ });
+ });
+
+ const updatedColumnDefinitions = updateColumnDefinitionsMaterialTypes(
+ columnDefinitions,
+ reactionMaterials,
+ true
+ );
+
+ const productIDs = getColumnDefinitionsMaterialIDs(updatedColumnDefinitions, 'products');
+ const productColumnDefinition = getColumnGroupChild(
+ updatedColumnDefinitions,
+ 'products',
+ `products.${productIDs[0]}`
+ );
+ expect(productColumnDefinition.cellDataType).toBe('gas');
+ expect(productColumnDefinition.entryDefs.currentEntry).toBe('duration');
+ expect(productColumnDefinition.entryDefs.displayUnit).toBe('Second(s)');
+
+ const reactantIDs = getColumnDefinitionsMaterialIDs(updatedColumnDefinitions, 'reactants');
+ const reactantColumnDefinition = getColumnGroupChild(
+ updatedColumnDefinitions,
+ 'reactants',
+ `reactants.${reactantIDs[0]}`
+ );
+ expect(reactantColumnDefinition.cellDataType).toBe('feedstock');
+ const { currentEntry } = reactantColumnDefinition.entryDefs;
+
+ expect(currentEntry).toBe('mass');
+ });
+ it('determines cell editability based on entry', async () => {
+ const colDef = {
+ field: 'foo',
+ entryDefs: {
+ currentEntry: 'equivalent',
+ displayUnit: null
+ }
+ };
+ const data = {
+ foo: {
+ aux: {
+ isReference: true,
+ gasType: 'off',
+ materialType: 'startingMaterials'
+ }
+ }
+ };
+ const params = { colDef, data };
+ expect(cellIsEditable(params)).toBe(false);
+ });
});
diff --git a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.spec.js b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.spec.js
index 7372dce5ca..23e4422ede 100644
--- a/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.spec.js
+++ b/spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsUtils.spec.js
@@ -32,9 +32,9 @@ describe('ReactionVariationsUtils', () => {
temperature: { value: '', unit: '°C' },
duration: { value: NaN, unit: 'Second(s)' },
});
- expect(Object.values(row.products).map((product) => product.aux.yield)).toEqual([100, 100]);
- expect(nonReferenceStartingMaterial.aux.equivalent).toBe(1);
- expect(reactant.aux.equivalent).toBe(1);
+ expect(Object.values(row.products).map((product) => product.yield.value)).toEqual([100, 100]);
+ expect(nonReferenceStartingMaterial.equivalent.value).toBe(1);
+ expect(reactant.equivalent.value).toBe(1);
});
it('copies a row in the variations table', async () => {
const reaction = await setUpReaction();
@@ -61,11 +61,11 @@ describe('ReactionVariationsUtils', () => {
{ ...referenceMaterial, mass: { ...referenceMaterial.mass, value: referenceMaterial.mass.value * 10 } },
reaction.hasPolymers()
);
- expect(Object.values(row.reactants)[0].aux.equivalent).toBeGreaterThan(
- Object.values(updatedRow.reactants)[0].aux.equivalent
+ expect(Object.values(row.reactants)[0].equivalent.value).toBeGreaterThan(
+ Object.values(updatedRow.reactants)[0].equivalent.value
);
- expect(Object.values(row.products)[0].aux.yield).toBeGreaterThan(
- Object.values(updatedRow.products)[0].aux.yield
+ expect(Object.values(row.products)[0].yield.value).toBeGreaterThan(
+ Object.values(updatedRow.products)[0].yield.value
);
});
it('updates the definition of a column', async () => {
@@ -74,7 +74,7 @@ describe('ReactionVariationsUtils', () => {
const field = `startingMaterials.${reactionMaterials.startingMaterials[0].id}`;
const columnDefinitions = Object.entries(reactionMaterials).map(([materialType, materials]) => ({
groupId: materialType,
- children: materials.map((material) => getMaterialColumnGroupChild(material, materialType, null))
+ children: materials.map((material) => getMaterialColumnGroupChild(material, materialType, null, false))
}));
expect(getColumnGroupChild(columnDefinitions, 'startingMaterials', field).cellDataType).toBe('material');
const updatedColumnDefinitions = updateColumnDefinitions(