From 911177df1f12b2e8c60518c3ba6a7bf59d214f3c Mon Sep 17 00:00:00 2001 From: Thomas Willheim Date: Fri, 5 Jul 2024 12:00:25 -0700 Subject: [PATCH] fixing issues with melting temp calcs and display, updating menu utils --- package.json | 2 +- packages/bio-parsers/package.json | 2 +- packages/ove/cypress/e2e/statusBar.spec.js | 7 +- packages/ove/package.json | 2 +- packages/ove/src/StatusBar/MeltingTemp.js | 24 ++++--- packages/sequence-utils/package.json | 2 +- packages/sequence-utils/src/calculateNebTm.js | 3 +- .../sequence-utils/src/calculateNebTm.test.js | 23 ++++-- packages/sequence-utils/src/calculateTm.js | 71 ++++++++++++++----- .../sequence-utils/src/calculateTm.test.js | 8 ++- packages/ui/package.json | 2 +- packages/ui/src/utils/menuUtils.js | 19 +++-- 12 files changed, 117 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 829996b2..01cf99c2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "MIT", "scripts": { "postinstall": "patch-package", - "prepare": "husky install", + "prepare": "husky", "e2e": "nx affected --base=master --head=HEAD -t e2e", "test": "nx affected -t test" }, diff --git a/packages/bio-parsers/package.json b/packages/bio-parsers/package.json index 8895ac02..131c182d 100644 --- a/packages/bio-parsers/package.json +++ b/packages/bio-parsers/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/bio-parsers", - "version": "0.4.18", + "version": "0.4.19", "type": "module", "dependencies": { "@gmod/gff": "^1.2.1", diff --git a/packages/ove/cypress/e2e/statusBar.spec.js b/packages/ove/cypress/e2e/statusBar.spec.js index 8a9ca02f..b81744dd 100644 --- a/packages/ove/cypress/e2e/statusBar.spec.js +++ b/packages/ove/cypress/e2e/statusBar.spec.js @@ -14,14 +14,17 @@ describe("statusBar", function () { cy.tgToggle("showMoleculeType", false); cy.get(`[data-test="veStatusBar-type"]`).should("not.exist"); }); - it("melting temp should be an option in the menu bar", function () { + it("melting temp should be an option in the menu bar, neb tm should work", function () { cy.visit(""); cy.selectRange(10, 30); cy.contains("Melting Temp").should("not.exist"); cy.get(".tg-menu-bar").contains("View").click(); cy.get(".bp3-menu-item").contains("Melting Temp").click(); - cy.contains("Melting Temp: 62.69").click(); + cy.contains("Melting Temp: 62.7").click(); cy.get(`[value="default"][checked]`); + cy.contains(`NEB Tm`).click(); + cy.contains("Melting Temp: 62.7").should("not.exist"); + cy.contains("Melting Temp: 64.6"); }); it(`if viewing a linear sequence in the circular view, there should be a little warning on the circular view telling the user that the sequence is linear`, () => { diff --git a/packages/ove/package.json b/packages/ove/package.json index 1c12eff2..f9346710 100644 --- a/packages/ove/package.json +++ b/packages/ove/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/ove", - "version": "0.5.19", + "version": "0.5.20", "main": "./src/index.js", "type": "module", "exports": { diff --git a/packages/ove/src/StatusBar/MeltingTemp.js b/packages/ove/src/StatusBar/MeltingTemp.js index d4b75f05..8f2a0dd0 100644 --- a/packages/ove/src/StatusBar/MeltingTemp.js +++ b/packages/ove/src/StatusBar/MeltingTemp.js @@ -3,7 +3,7 @@ import { Button, Icon, Popover, RadioGroup } from "@blueprintjs/core"; import { calculateTm, calculateNebTm } from "@teselagen/sequence-utils"; -import { isString } from "lodash-es"; +import { isNumber, isString } from "lodash-es"; import { popoverOverflowModifiers } from "@teselagen/ui"; import useTmType from "../utils/useTmType"; @@ -16,13 +16,17 @@ export default function MeltingTemp({ ) }) { + const [primerConc /* , setPrimerConcentration */] = React.useState(0.0000005); + const [monovalentCationConc /* , setMonovalentCationConc */] = + React.useState(0.05); const [tmType, setTmType] = useTmType(); - const tm = ( - { - default: calculateTm, - neb_tm: calculateNebTm - }[tmType] || calculateTm - )((sequence || "").toLowerCase()); + let tm = (tmType === "neb_tm" ? calculateNebTm : calculateTm)(sequence, { + monovalentCationConc, + primerConc + }); + if (isNumber(tm)) { + tm = tm.toFixed(1); + } const hasWarning = isString(tm) && tm.length > 7 && tm; return ( @@ -34,7 +38,7 @@ export default function MeltingTemp({ algorithms @@ -43,8 +47,8 @@ export default function MeltingTemp({ setTmType(e.target.value)} selectedValue={tmType} diff --git a/packages/sequence-utils/package.json b/packages/sequence-utils/package.json index 909930a2..86d69cc4 100644 --- a/packages/sequence-utils/package.json +++ b/packages/sequence-utils/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/sequence-utils", - "version": "0.3.24", + "version": "0.3.25", "type": "module", "dependencies": { "bson-objectid": "^2.0.4", diff --git a/packages/sequence-utils/src/calculateNebTm.js b/packages/sequence-utils/src/calculateNebTm.js index a7cefe60..1487a1e1 100644 --- a/packages/sequence-utils/src/calculateNebTm.js +++ b/packages/sequence-utils/src/calculateNebTm.js @@ -9,8 +9,7 @@ import calculatePercentGc from "./calculatePercentGC"; // primer concentration & monovalent cation concentration in M export default function calculateNebTm( sequence, - primerConc, - { monovalentCationConc } = {} + { monovalentCationConc = 0.05, primerConc = 0.0000005 } = {} ) { try { const checkForDegenerateBases = /[^atgc]/i.test(sequence); diff --git a/packages/sequence-utils/src/calculateNebTm.test.js b/packages/sequence-utils/src/calculateNebTm.test.js index 2dff8c69..6cf654ef 100644 --- a/packages/sequence-utils/src/calculateNebTm.test.js +++ b/packages/sequence-utils/src/calculateNebTm.test.js @@ -1,31 +1,40 @@ import assert from "assert"; -import calculateTm from "./calculateNebTm"; +import calculateNebTm from "./calculateNebTm"; +// import calculateTm from "./calculateTm"; describe("calculate Tm based on SantaLucia 1998 & Owczarzy 2004", () => { it("should return the melting temperature of a given sequence, if no degenerate bases are present", () => { const options = { // 50 mM KCl in Q5 protocol - monovalentCationConc: 0.05 + monovalentCationConc: 0.05, + primerConc: 0.0000005 }; + // console.log(`calculateNebTm("AGCGGATAACAATTTCACACAGGA", options),:`,calculateNebTm("AGCGGATAACAATTTCACACAGGA", options),) + // console.log(`calculateTm("AGCGGATAACAATTTCACACAGGA", options),:`,calculateTm("AGCGGATAACAATTTCACACAGGA", options),) + // console.log(`calculateTm("AGCGGATAnbACdAATTTCACACANNGGA", options),:`,calculateTm("AGCGGATAACAATTTCACACAGGA", options),) // primer concentration in Q5 protocol is 500 nM assert.equal( - calculateTm("AGCGGATAACAATTTCACACAGGA", 0.0000005, options), + calculateNebTm("AGCGGATAACAATTTCACACAGGA", options), 65.8994505801345 ); assert.equal( - calculateTm("AGCGGATAACAATTTCAC", 0.0000005, options), + calculateNebTm("AGCGGATAACAATTTCAC", options), 56.11037835109477 ); assert.equal( - calculateTm("AGCGGATAACAATTTcac", 0.0000005, options), + calculateNebTm("AGCGGATAACAATTTcac", options), 56.11037835109477 ); assert.equal( - calculateTm("AGCGGNNN", 0.0000005, options), + calculateNebTm("ataataccgcgccacatagc", options), + 65.03019485849268 + ); + assert.equal( + calculateNebTm("AGCGGNNN", options), "Error calculating Tm for sequence AGCGGNNN: Error: Degenerate bases prohibited in Tm calculation of sequence AGCGGNNN" ); assert.equal( - calculateTm("AGCGGnnn", 0.0000005, options), + calculateNebTm("AGCGGnnn", options), "Error calculating Tm for sequence AGCGGnnn: Error: Degenerate bases prohibited in Tm calculation of sequence AGCGGnnn" ); }); diff --git a/packages/sequence-utils/src/calculateTm.js b/packages/sequence-utils/src/calculateTm.js index bc9cca81..3481014e 100644 --- a/packages/sequence-utils/src/calculateTm.js +++ b/packages/sequence-utils/src/calculateTm.js @@ -11,8 +11,8 @@ const calcTmMethods = { A: -10.8, // Helix initiation for deltaS R: 1.987, // Gas constant (cal/(K*mol)). - C: 0.5e-6, // Oligo concentration. 0.5uM is typical for PCR. - Na: 50e-3, // Monovalent salt concentration. 50mM is typical for PCR. + primerConc: 0.0000005, // Oligo concentration. 0.5uM is typical for PCR. + monovalentCationConc: 0.05, // Monovalent salt concentration. 50mM is typical for PCR. /** * Calculates temperature for DNA sequence using a given algorithm. @@ -20,11 +20,17 @@ const calcTmMethods = { * type - Either Teselagen.bio.tools.TemperatureCalculator.TABLE_BRESLAUER, TABLE_SUGIMOTO, or TABLE_UNIFIED * A - Helix initation for deltaS. Defaults to -10.8. * R - The gas constant, in cal/(K*mol). Defaults to 0.5e-6M. - * Na - THe monovalent salt concentration. Defaults to 50e-3M. + * monovalentCationConc - THe monovalent salt concentration. Defaults to 50e-3M. * return - Temperature for the given sequence, in Celsius. */ - calculateTemperature: function (sequence, type, A, R, C, Na) { + calculateTemperature: function ( + _sequence, + { type, A, R, primerConc, monovalentCationConc } = {} + ) { + const sequence = _sequence.toLowerCase(); if (typeof type === "undefined") { + // type = this.TABLE_SUGIMOTO ; + // type = this.TABLE_UNIFIED; type = this.TABLE_BRESLAUER; } else if ( type != this.TABLE_BRESLAUER && @@ -40,11 +46,11 @@ const calcTmMethods = { if (!R) { R = this.R; } - if (!C) { - C = this.C; + if (!primerConc) { + primerConc = this.primerConc; } - if (!Na) { - Na = this.Na; + if (!monovalentCationConc) { + monovalentCationConc = this.monovalentCationConc; } const sequenceLength = sequence.length; @@ -56,7 +62,7 @@ const calcTmMethods = { const deltaHTable = this.getDeltaHTable(type); const deltaSTable = this.getDeltaSTable(type); - const neighbors = []; // List goes in order: aa, at, ac, ag, tt, ta, tc, tg, cc, ca, ct, cg, gg, gt, gc + const neighbors = []; // List goes in order: aa, at, ac, ag, tt, ta, tc, tg, cc, ca, ct, cg, gg, ga, gt, gc neighbors.push(this.calculateReps(sequence, "aa")); neighbors.push(this.calculateNumberOfOccurrences(sequence, "at")); @@ -87,16 +93,12 @@ const calcTmMethods = { } const temperature = - (-1000.0 * sumDeltaH) / (A + -sumDeltaS + R * Math.log(C / 4.0)) - + (-1000.0 * sumDeltaH) / + (A + -sumDeltaS + R * Math.log(primerConc / 4.0)) - 273.15 + - 16.6 * Math.LOG10E * Math.log(Na); + 16.6 * Math.LOG10E * Math.log(monovalentCationConc); - // If temperature is negative then return 0. - if (temperature < 0) { - return 0; - } - - return temperature.toFixed(2); + return temperature; }, /** @@ -125,6 +127,41 @@ const calcTmMethods = { return null; } }, + // "AA/TT": -7.9, 7.9 + // "AT/TA": -7.2, 7.2 + // "AC/TG": -8.4, 8.4 + // "AG/TC": -7.8, 7.8 + // "TT/AA": -7.9, 7.9 + // "TA/AT": -7.2, 7.2 + // "TG/AC": -8.5, 8.2 + // "TC/AG": -8.2, 8.5 + // "CC/GG": -8.0, 8.0 + // "CA/GT": -8.5, 8.5 + // "CT/GA": -7.8, 7.8 + // "CG/GC": -10.6, 10.6 + // "GG/CC": -8.0, 8.0 + // "GA/CT": -8.2, 8.2, + // "GT/CA": -8.4, 8.4 + // "GC/CG": -9.8, 9.8 + + // aa, at, ac, ag, tt, ta, tc, tg, cc, ca, ct, cg, gg, ga, gt, gc + + // "AA/TT": -22.2,22.2, + // "AT/TA": -20.4,20.4, + // "AC/TG": -22.4,22.4, + // "AG/TC": -21.0,21.0, + // "TT/AA": -22.2,22.2, + // "TA/AT": -21.3,21.3, + // "TC/AG": -22.2,22.2, + // "TG/AC": -22.7,22.7, + // "CC/GG": -19.9,19.9, + // "CA/GT": -22.7,22.7, + // "CT/GA": -21.0,21.0, + // "CG/GC": -27.2,27.2, + // "GG/CC": -19.9,19.9, + // "GT/CA": -22.4,22.2, + // "GA/CT": -22.2,22.4, + // "GC/CG": -24.4,24.4 /** * @private diff --git a/packages/sequence-utils/src/calculateTm.test.js b/packages/sequence-utils/src/calculateTm.test.js index 1e496287..de1a4769 100644 --- a/packages/sequence-utils/src/calculateTm.test.js +++ b/packages/sequence-utils/src/calculateTm.test.js @@ -2,6 +2,12 @@ import calculateTm from "./calculateTm"; import assert from "assert"; describe("calculateTm", () => { it("should calculate the correct tm", () => { - assert.equal(calculateTm("atagagaggga"), 26.21); + assert.equal(calculateTm("atagagaggga"), 26.211404758492115); + assert.equal(calculateTm("AGCGGATAACAATTTCACACAGGA"), 67.27154960706082); + assert.equal(calculateTm("AGCGGATAACAATTTCAC"), 54.91103113095034); + assert.equal(calculateTm("AGCGGATAACAATTTcac"), 54.91103113095034); + assert.equal(calculateTm("ataataccgcgccacatagc"), 63.51394755261396); + assert.equal(calculateTm("AGCGGNNN"), -5.0392194500109255); + assert.equal(calculateTm("AGCGGnnn"), -5.0392194500109255); }); }); diff --git a/packages/ui/package.json b/packages/ui/package.json index 801b7967..1987d061 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@teselagen/ui", - "version": "0.4.16", + "version": "0.4.17", "main": "./src/index.js", "type": "module", "exports": { diff --git a/packages/ui/src/utils/menuUtils.js b/packages/ui/src/utils/menuUtils.js index cb931da0..53c4d2ac 100644 --- a/packages/ui/src/utils/menuUtils.js +++ b/packages/ui/src/utils/menuUtils.js @@ -23,7 +23,14 @@ import { import fuzzysearch from "fuzzysearch"; import classNames from "classnames"; // https://github.com/palantir/blueprint/issues/2820 -export function MenuItemLink({ text, onClick, icon, navTo, active }) { +export function MenuItemLink({ text, onClick, icon, navTo, active, disabled }) { + if (disabled) { + return ( +
  • + +
  • + ); + } const handleLinkClick = e => { e.target.closest(`.${Classes.POPOVER_DISMISS}`).click(); }; @@ -71,7 +78,6 @@ export const EnhancedMenuItem = compose( if (navTo) { MenuItemComp = MenuItemLink; } - return ( f(v, context), def); let out; - if (item.divider !== undefined) { - out = ; + out = ( + + ); } else { const ItemComponent = item.component || EnhancedMenuItem; out = (