Skip to content

Commit

Permalink
fixing issues with melting temp calcs and display, updating menu utils
Browse files Browse the repository at this point in the history
  • Loading branch information
tnrich committed Jul 5, 2024
1 parent 39d16f1 commit 911177d
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 48 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/bio-parsers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@teselagen/bio-parsers",
"version": "0.4.18",
"version": "0.4.19",
"type": "module",
"dependencies": {
"@gmod/gff": "^1.2.1",
Expand Down
7 changes: 5 additions & 2 deletions packages/ove/cypress/e2e/statusBar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`, () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/ove/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@teselagen/ove",
"version": "0.5.19",
"version": "0.5.20",
"main": "./src/index.js",
"type": "module",
"exports": {
Expand Down
24 changes: 14 additions & 10 deletions packages/ove/src/StatusBar/MeltingTemp.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -16,13 +16,17 @@ export default function MeltingTemp({
</Button>
)
}) {
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 (
<WrapperToUse dataTest="veStatusBar-selection-tm">
Expand All @@ -34,7 +38,7 @@ export default function MeltingTemp({
<a
rel="noopener noreferrer"
target="_blank"
href="https://github.com/TeselaGen/@teselagen/sequence-utils"
href="https://github.com/TeselaGen/tg-oss/blob/master/packages/sequence-utils/src/calculateNebTm.js"
>
algorithms
</a>
Expand All @@ -43,8 +47,8 @@ export default function MeltingTemp({
<RadioGroup
label="Choose Tm Type:"
options={[
{ value: "default", label: "Default Tm" },
{ value: "neb_tm", label: "NEB Tm" }
{ value: "default", label: "Default Tm (Breslauer)" },
{ value: "neb_tm", label: "NEB Tm (SantaLucia)" }
]}
onChange={e => setTmType(e.target.value)}
selectedValue={tmType}
Expand Down
2 changes: 1 addition & 1 deletion packages/sequence-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@teselagen/sequence-utils",
"version": "0.3.24",
"version": "0.3.25",
"type": "module",
"dependencies": {
"bson-objectid": "^2.0.4",
Expand Down
3 changes: 1 addition & 2 deletions packages/sequence-utils/src/calculateNebTm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
23 changes: 16 additions & 7 deletions packages/sequence-utils/src/calculateNebTm.test.js
Original file line number Diff line number Diff line change
@@ -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"
);
});
Expand Down
71 changes: 54 additions & 17 deletions packages/sequence-utils/src/calculateTm.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,26 @@ 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.
* sequence - The DNA sequence to use.
* 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 &&
Expand All @@ -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;
Expand All @@ -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"));
Expand Down Expand Up @@ -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;
},

/**
Expand Down Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion packages/sequence-utils/src/calculateTm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@teselagen/ui",
"version": "0.4.16",
"version": "0.4.17",
"main": "./src/index.js",
"type": "module",
"exports": {
Expand Down
19 changes: 15 additions & 4 deletions packages/ui/src/utils/menuUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<li className={Classes.POPOVER_DISMISS}>
<MenuItem icon={icon} disabled={true} text={text} />
</li>
);
}
const handleLinkClick = e => {
e.target.closest(`.${Classes.POPOVER_DISMISS}`).click();
};
Expand Down Expand Up @@ -71,7 +78,6 @@ export const EnhancedMenuItem = compose(
if (navTo) {
MenuItemComp = MenuItemLink;
}

return (
<MenuItemComp
popoverProps={{
Expand Down Expand Up @@ -215,9 +221,14 @@ export const DynamicMenuItem = ({
...(doNotEnhanceTopLevelItem ? [ident] : enhancers)
].reduce((v, f) => f(v, context), def);
let out;

if (item.divider !== undefined) {
out = <MenuDivider {...(item.divider ? { title: item.divider } : {})} />;
out = (
<MenuDivider
{...(item.divider
? { title: item.divider, className: item.className }
: {})}
/>
);
} else {
const ItemComponent = item.component || EnhancedMenuItem;
out = (
Expand Down

0 comments on commit 911177d

Please sign in to comment.