gramene-search 2.33.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -1
- package/dist/index.js +200 -116
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -46,4 +46,39 @@ Run the sorghum demo against a swagger that serves <code>/primers</code>:
|
|
|
46
46
|
PRIMERS_API=https://data.sorghumbase.org/sorghum_v11a SUBSITE=sorghum npx parcel src/sorghum.html --port 1234
|
|
47
47
|
```
|
|
48
48
|
In the sorghum demo (<code>src/demo.js</code>) the tab is shown only when <code>PRIMERS_API</code> is set;
|
|
49
|
-
it becomes <code>details.primers.apiBase</code>.
|
|
49
|
+
it becomes <code>details.primers.apiBase</code>.
|
|
50
|
+
|
|
51
|
+
## Expression tab
|
|
52
|
+
The Expression gene-detail tab's "All Studies" and "Paralogs" sub-tabs draw Expression Atlas heatmaps
|
|
53
|
+
(with the anatomogram) in the page using <code>ExpressionAtlasHeatmap</code> from
|
|
54
|
+
<a href="https://github.com/warelab/atlas-heatmap">gramene-atlas-heatmap</a>, warelab's React 18 fork of
|
|
55
|
+
EBI's atlas-heatmap; the anatomogram comes from
|
|
56
|
+
<a href="https://github.com/warelab/anatomogram">gramene-anatomogram</a>. They replace the old
|
|
57
|
+
iframe to <code>dev.gramene.org/static/atlasWidget.html</code>.
|
|
58
|
+
|
|
59
|
+
- <code>atlasUrl</code> in the site configuration names the gramene-swagger <code>/gxa/</code> instance to query,
|
|
60
|
+
e.g. <code>'https://data.sorghumbase.org/sorghum_v11/gxa/'</code>. Unset, the tab queries
|
|
61
|
+
<code>https://data.sorghumbase.org/auth_testing/gxa/</code>, the old widget's default.
|
|
62
|
+
- All Studies sends the gene's <code>atlas_id</code> (or its id); Paralogs sends the within-species paralogs
|
|
63
|
+
for the experiment picked in the selector.
|
|
64
|
+
- Links open in a new tab. <code>resolveUrl</code> in <code>Expression.js</code> points row, experiment,
|
|
65
|
+
"more information", Expression Atlas and download links at EBI, because gramene-swagger returns
|
|
66
|
+
relative gene links and an empty <code>geneQuery</code>.
|
|
67
|
+
- The chosen sub-tab and experiment are kept in <code>uiViewState.byGene[geneId].expression</code> and saved
|
|
68
|
+
with shared views.
|
|
69
|
+
- Styles are injected at runtime; Bootstrap 5 CSS and react-bootstrap 2 come from the host.
|
|
70
|
+
|
|
71
|
+
<code>gramene-atlas-heatmap</code> is a dependency (<code>^6.0.0</code>) and brings in
|
|
72
|
+
<code>gramene-anatomogram</code>. To try unreleased fork changes, install packed tarballs of both packages over
|
|
73
|
+
the registry versions (not <code>npm link</code> or <code>file:</code>, which load a second React); the next
|
|
74
|
+
<code>npm install</code> puts the registry versions back.
|
|
75
|
+
```bash
|
|
76
|
+
cd ../anatomogram && npm run pack:local # gramene-anatomogram-3.0.0.tgz
|
|
77
|
+
cd ../atlas-heatmap && npm run pack:local # gramene-atlas-heatmap-6.0.0.tgz
|
|
78
|
+
cd ../gramene-search && npm install --no-save ../anatomogram/gramene-anatomogram-3.0.0.tgz \
|
|
79
|
+
../atlas-heatmap/gramene-atlas-heatmap-6.0.0.tgz && rm -rf .parcel-cache*
|
|
80
|
+
```
|
|
81
|
+
Run the sorghum demo against another atlas instance:
|
|
82
|
+
```bash
|
|
83
|
+
ATLAS_URL=https://data.sorghumbase.org/sorghum_v11/gxa/ SUBSITE=sorghum npx parcel src/sorghum.html --port 1235
|
|
84
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ var $gXNCa$reactswitch = require("react-switch");
|
|
|
17
17
|
var $gXNCa$reacticonsio5 = require("react-icons/io5");
|
|
18
18
|
var $gXNCa$reacticonsbs = require("react-icons/bs");
|
|
19
19
|
var $gXNCa$reacticonsgr = require("react-icons/gr");
|
|
20
|
+
var $gXNCa$grameneatlasheatmap = require("gramene-atlas-heatmap");
|
|
20
21
|
var $gXNCa$gramenegenetreevis = require("gramene-genetree-vis");
|
|
21
22
|
var $gXNCa$gramenetreesclientsrcgenetree = require("gramene-trees-client/src/genetree");
|
|
22
23
|
var $gXNCa$tbrowse = require("tbrowse");
|
|
@@ -7836,6 +7837,7 @@ var $541b8b0d8c5501d2$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
7836
7837
|
|
|
7837
7838
|
|
|
7838
7839
|
|
|
7840
|
+
|
|
7839
7841
|
// eFP Browser embed — was its own package (gramene-efp-browser@1.0.11),
|
|
7840
7842
|
// absorbed here on 2026-05-29 to drop the unmaintained nwb/webpack-4 build
|
|
7841
7843
|
// pipeline and remove a dependency that needed --openssl-legacy-provider.
|
|
@@ -8056,56 +8058,71 @@ function $59a241f3912e631e$export$1bc0f3596ef2efe9(gene) {
|
|
|
8056
8058
|
}
|
|
8057
8059
|
|
|
8058
8060
|
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8061
|
+
// "All Studies" and "Paralogs" draw Expression Atlas heatmaps in the page with
|
|
8062
|
+
// gramene-atlas-heatmap (warelab's React 18 fork of EBI's atlas-heatmap; the
|
|
8063
|
+
// anatomogram comes from gramene-anatomogram). configuration.atlasUrl names the
|
|
8064
|
+
// gramene-swagger /gxa/ instance to query; sites that leave it unset get the
|
|
8065
|
+
// instance the old dev.gramene.org iframe widget fell back to.
|
|
8066
|
+
const $9e29a4f60318db7a$var$DEFAULT_ATLAS_URL = 'https://data.sorghumbase.org/auth_testing/gxa/';
|
|
8067
|
+
const $9e29a4f60318db7a$var$EBI_GXA = 'https://www.ebi.ac.uk/gxa/';
|
|
8068
|
+
const $9e29a4f60318db7a$var$genesOf = (query)=>query && query.gene ? query.gene.split(' ') : [];
|
|
8069
|
+
const $9e29a4f60318db7a$var$editSearch = (url, edit)=>{
|
|
8070
|
+
try {
|
|
8071
|
+
const u = new URL(url);
|
|
8072
|
+
edit(u.searchParams);
|
|
8073
|
+
return u.href;
|
|
8074
|
+
} catch (e) {
|
|
8075
|
+
return undefined;
|
|
8076
|
+
}
|
|
8077
|
+
};
|
|
8078
|
+
// gramene-swagger echoes geneQuery back as [null,...], returns relative row uris
|
|
8079
|
+
// (genes/<id>) that only some instances redirect to EBI, and atlasUrl itself is
|
|
8080
|
+
// not a browsable page, so point the heatmap's links at EBI's Expression Atlas.
|
|
8081
|
+
// undefined keeps the heatmap's own URL.
|
|
8082
|
+
const $9e29a4f60318db7a$var$resolveUrl = (kind, url, context)=>{
|
|
8083
|
+
const genes = $9e29a4f60318db7a$var$genesOf(context.query);
|
|
8084
|
+
const withGenes = (params)=>params.set('geneQuery', JSON.stringify(genes.map((value)=>({
|
|
8085
|
+
value: value
|
|
8086
|
+
}))));
|
|
8087
|
+
switch(kind){
|
|
8088
|
+
case 'row':
|
|
8089
|
+
{
|
|
8090
|
+
const uri = context.row && context.row.uri;
|
|
8091
|
+
return uri && !/^https?:/i.test(uri) ? new URL(uri, $9e29a4f60318db7a$var$EBI_GXA).href : undefined;
|
|
8092
|
+
}
|
|
8093
|
+
case 'atlas':
|
|
8094
|
+
return $9e29a4f60318db7a$var$EBI_GXA;
|
|
8095
|
+
case 'experiment':
|
|
8096
|
+
return $9e29a4f60318db7a$var$editSearch(url, withGenes);
|
|
8097
|
+
case 'moreInformation':
|
|
8098
|
+
return context.experiment ? $9e29a4f60318db7a$var$editSearch(url, withGenes) : `${$9e29a4f60318db7a$var$EBI_GXA}genes/${encodeURIComponent(genes[0])}`;
|
|
8099
|
+
case 'download':
|
|
8100
|
+
return $9e29a4f60318db7a$var$editSearch(url, (params)=>params.delete('geneQuery'));
|
|
8101
|
+
default:
|
|
8102
|
+
return undefined;
|
|
8103
|
+
}
|
|
8104
|
+
};
|
|
8105
|
+
const $9e29a4f60318db7a$var$HEATMAP_OPTIONS = {
|
|
8106
|
+
showAnatomogram: true,
|
|
8107
|
+
isWidget: true,
|
|
8108
|
+
showControlMenu: true,
|
|
8109
|
+
linkTarget: '_blank',
|
|
8110
|
+
resolveUrl: $9e29a4f60318db7a$var$resolveUrl
|
|
8111
|
+
};
|
|
8112
|
+
const $9e29a4f60318db7a$var$studyLabel = (e)=>`${e.type}:${e.description || e._id}`;
|
|
8081
8113
|
const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
8082
8114
|
const geneId = props.searchResult.id;
|
|
8083
8115
|
const gene = props.geneDocs[geneId];
|
|
8084
8116
|
// User-selected view state (active sub-tab, chosen GXA experiment, chosen eFP
|
|
8085
8117
|
// study) lives in the uiViewState bundle keyed by geneId, so the shareable-
|
|
8086
|
-
// views snapshot can round-trip it.
|
|
8087
|
-
// toggle stay in local state. Defaults match the old local-state initials.
|
|
8118
|
+
// views snapshot can round-trip it.
|
|
8088
8119
|
const expr = props.uiViewState && props.uiViewState.byGene[geneId] && props.uiViewState.byGene[geneId].expression || {};
|
|
8089
8120
|
const activeTab = expr.activeTab || 'gene';
|
|
8090
8121
|
const atlasExperiment = expr.atlasExperiment || null;
|
|
8091
|
-
const
|
|
8122
|
+
const setExpression = (patch)=>props.doSetExpressionState({
|
|
8092
8123
|
geneId: geneId,
|
|
8093
|
-
patch:
|
|
8094
|
-
activeTab: k
|
|
8095
|
-
}
|
|
8124
|
+
patch: patch
|
|
8096
8125
|
});
|
|
8097
|
-
const setAtlasExperiment = (v)=>props.doSetExpressionState({
|
|
8098
|
-
geneId: geneId,
|
|
8099
|
-
patch: {
|
|
8100
|
-
atlasExperiment: v
|
|
8101
|
-
}
|
|
8102
|
-
});
|
|
8103
|
-
const [atlasExperimentList, setAtlasExperimentList] = (0, $gXNCa$react.useState)([]);
|
|
8104
|
-
const [atlasFacets, setAtlasFacets] = (0, $gXNCa$react.useState)(null);
|
|
8105
|
-
const [isLocal, setIsLocal] = (0, $gXNCa$react.useState)(false);
|
|
8106
|
-
const handleLocalAPIChange = (event)=>{
|
|
8107
|
-
setIsLocal(event.target.checked);
|
|
8108
|
-
};
|
|
8109
8126
|
// The expressionStudies resource is otherwise fetched only when a top-level
|
|
8110
8127
|
// expression view (exprViz/expression/export) is on — but this per-gene
|
|
8111
8128
|
// Expression detail also needs it (the Paralogs sub-tab's experiment list and
|
|
@@ -8116,96 +8133,105 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
|
8116
8133
|
}, [
|
|
8117
8134
|
props.expressionStudies
|
|
8118
8135
|
]);
|
|
8136
|
+
const studies = props.expressionStudies && props.expressionStudies[Math.floor(gene.taxon_id / 1000)];
|
|
8137
|
+
const inGxa = props.searchResult.expressed_in_gxa_attr_ss;
|
|
8138
|
+
const experiments = (0, $gXNCa$react.useMemo)(()=>{
|
|
8139
|
+
if (!studies) return [];
|
|
8140
|
+
const inGxaSet = inGxa && new Set(inGxa);
|
|
8141
|
+
// copy before sorting: the studies array belongs to the expressionStudies bundle
|
|
8142
|
+
const list = inGxaSet ? studies.filter((e)=>inGxaSet.has(e._id)) : studies.slice();
|
|
8143
|
+
return list.sort((a, b)=>$9e29a4f60318db7a$var$studyLabel(a) < $9e29a4f60318db7a$var$studyLabel(b) ? -1 : 1);
|
|
8144
|
+
}, [
|
|
8145
|
+
studies,
|
|
8146
|
+
inGxa
|
|
8147
|
+
]);
|
|
8148
|
+
// Only pick a default experiment when the user (or a restored snapshot)
|
|
8149
|
+
// hasn't already chosen one — otherwise we'd clobber a saved selection
|
|
8150
|
+
// the moment the studies list loads.
|
|
8119
8151
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
8120
|
-
if (
|
|
8121
|
-
const
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
Baseline: {}
|
|
8126
|
-
};
|
|
8127
|
-
let eList = props.expressionStudies[tid].sort((a, b)=>{
|
|
8128
|
-
const a_name = `${a.type}:${a.description || a._id}`;
|
|
8129
|
-
const b_name = `${b.type}:${b.description || b._id}`;
|
|
8130
|
-
return a_name < b_name ? -1 : 1;
|
|
8131
|
-
});
|
|
8132
|
-
if (props.searchResult.hasOwnProperty('expressed_in_gxa_attr_ss')) {
|
|
8133
|
-
const in_gxa = new Set(props.searchResult.expressed_in_gxa_attr_ss);
|
|
8134
|
-
eList = eList.filter((e)=>in_gxa.has(e._id));
|
|
8135
|
-
}
|
|
8136
|
-
eList.forEach((e)=>{
|
|
8137
|
-
e.factors.forEach((factor)=>facets[e.type][factor] = 1);
|
|
8138
|
-
});
|
|
8139
|
-
setAtlasExperimentList(eList);
|
|
8140
|
-
setAtlasFacets(facets);
|
|
8141
|
-
// Only pick a default experiment when the user (or a restored snapshot)
|
|
8142
|
-
// hasn't already chosen one — otherwise we'd clobber a saved selection
|
|
8143
|
-
// the moment the studies list loads.
|
|
8144
|
-
if (!atlasExperiment) {
|
|
8145
|
-
let refExp = eList.filter((e)=>e.isRef);
|
|
8146
|
-
if (refExp.length === 1) setAtlasExperiment(refExp[0]._id);
|
|
8147
|
-
else // no reference experiment - choose first
|
|
8148
|
-
setAtlasExperiment(eList[0]._id);
|
|
8149
|
-
}
|
|
8150
|
-
}
|
|
8152
|
+
if (atlasExperiment || experiments.length === 0) return;
|
|
8153
|
+
const refExp = experiments.filter((e)=>e.isRef);
|
|
8154
|
+
setExpression({
|
|
8155
|
+
atlasExperiment: (refExp.length === 1 ? refExp[0] : experiments[0])._id
|
|
8156
|
+
});
|
|
8151
8157
|
}, [
|
|
8152
|
-
|
|
8158
|
+
experiments,
|
|
8159
|
+
atlasExperiment
|
|
8153
8160
|
]);
|
|
8154
|
-
|
|
8155
|
-
// widget falls back to its own default when `atlasUrl` is absent, so sites
|
|
8156
|
-
// that don't configure one are unaffected — only pass it when set.
|
|
8157
|
-
const atlasUrl = props.configuration && props.configuration.atlasUrl || null;
|
|
8158
|
-
const atlasParam = atlasUrl ? `&atlasUrl=${encodeURIComponent(atlasUrl)}` : '';
|
|
8159
|
-
let paralogs_url;
|
|
8160
|
-
let gene_url = `https://dev.gramene.org/static/atlasWidget.html?genes=${gene.atlas_id || gene._id}&localAPI=${isLocal}${atlasParam}`;
|
|
8161
|
-
let paralogs = [];
|
|
8162
|
-
const haveParalogs = props.grameneParalogs && props.grameneParalogs[gene._id];
|
|
8163
|
-
if (haveParalogs) paralogs = props.grameneParalogs[gene._id];
|
|
8161
|
+
const paralogs = props.grameneParalogs && props.grameneParalogs[gene._id];
|
|
8164
8162
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
8165
|
-
if (!
|
|
8163
|
+
if (!paralogs && gene.homology) props.doRequestParalogs(gene._id, gene.homology.supertree, gene.taxon_id);
|
|
8166
8164
|
}, [
|
|
8167
8165
|
gene._id,
|
|
8168
|
-
|
|
8166
|
+
!!paralogs
|
|
8169
8167
|
]);
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8168
|
+
const atlasUrl = props.configuration && props.configuration.atlasUrl || $9e29a4f60318db7a$var$DEFAULT_ATLAS_URL;
|
|
8169
|
+
const geneQuery = gene.atlas_id || gene._id;
|
|
8170
|
+
const paralogQuery = paralogs && paralogs.length > 0 ? paralogs.join(' ') : null;
|
|
8171
|
+
const allStudiesQuery = (0, $gXNCa$react.useMemo)(()=>({
|
|
8172
|
+
gene: geneQuery
|
|
8173
|
+
}), [
|
|
8174
|
+
geneQuery
|
|
8175
|
+
]);
|
|
8176
|
+
const paralogsQuery = (0, $gXNCa$react.useMemo)(()=>({
|
|
8177
|
+
gene: paralogQuery
|
|
8178
|
+
}), [
|
|
8179
|
+
paralogQuery
|
|
8180
|
+
]);
|
|
8181
|
+
const knownExperiment = experiments.some((e)=>e._id === atlasExperiment);
|
|
8182
|
+
// Only the active sub-tab's heatmap is mounted; the keys give each query its
|
|
8183
|
+
// own fetch, zoom and filters.
|
|
8174
8184
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Tabs), {
|
|
8175
8185
|
activeKey: activeTab,
|
|
8176
|
-
onSelect: (k)=>
|
|
8186
|
+
onSelect: (k)=>setExpression({
|
|
8187
|
+
activeTab: k
|
|
8188
|
+
}),
|
|
8177
8189
|
children: [
|
|
8178
|
-
|
|
8190
|
+
paralogQuery && atlasExperiment && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Tab), {
|
|
8179
8191
|
tabClassName: "gxa",
|
|
8180
8192
|
eventKey: "paralogs",
|
|
8181
|
-
title:
|
|
8193
|
+
title: "Paralogs",
|
|
8182
8194
|
children: [
|
|
8183
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
8195
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Select, {
|
|
8196
|
+
className: "mb-2",
|
|
8184
8197
|
"aria-label": "experiment selector",
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8198
|
+
value: atlasExperiment,
|
|
8199
|
+
onChange: (e)=>setExpression({
|
|
8200
|
+
atlasExperiment: e.target.value
|
|
8201
|
+
}),
|
|
8202
|
+
children: [
|
|
8203
|
+
!knownExperiment && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("option", {
|
|
8204
|
+
value: atlasExperiment,
|
|
8205
|
+
children: atlasExperiment
|
|
8206
|
+
}),
|
|
8207
|
+
experiments.map((e)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("option", {
|
|
8208
|
+
value: e._id,
|
|
8209
|
+
children: [
|
|
8210
|
+
e.type,
|
|
8211
|
+
": ",
|
|
8212
|
+
e.description || e._id
|
|
8213
|
+
]
|
|
8214
|
+
}, e._id))
|
|
8215
|
+
]
|
|
8196
8216
|
}),
|
|
8197
|
-
activeTab === "paralogs" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($
|
|
8198
|
-
|
|
8199
|
-
|
|
8217
|
+
activeTab === "paralogs" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneatlasheatmap.ExpressionAtlasHeatmap), {
|
|
8218
|
+
...$9e29a4f60318db7a$var$HEATMAP_OPTIONS,
|
|
8219
|
+
atlasUrl: atlasUrl,
|
|
8220
|
+
query: paralogsQuery,
|
|
8221
|
+
experiment: atlasExperiment
|
|
8222
|
+
}, `${atlasUrl} ${atlasExperiment} ${paralogQuery}`)
|
|
8200
8223
|
]
|
|
8201
8224
|
}, "gxaparalogs"),
|
|
8202
8225
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
|
|
8203
8226
|
tabClassName: "gxa",
|
|
8204
8227
|
eventKey: "gene",
|
|
8205
8228
|
title: "All Studies",
|
|
8206
|
-
children: activeTab === "gene" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($
|
|
8207
|
-
|
|
8208
|
-
|
|
8229
|
+
children: activeTab === "gene" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneatlasheatmap.ExpressionAtlasHeatmap), {
|
|
8230
|
+
...$9e29a4f60318db7a$var$HEATMAP_OPTIONS,
|
|
8231
|
+
atlasUrl: atlasUrl,
|
|
8232
|
+
query: allStudiesQuery,
|
|
8233
|
+
experiment: false
|
|
8234
|
+
}, `${atlasUrl} all ${geneQuery}`)
|
|
8209
8235
|
}, "gxa"),
|
|
8210
8236
|
(0, $59a241f3912e631e$export$1bc0f3596ef2efe9)(gene) && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
|
|
8211
8237
|
tabClassName: "eFP",
|
|
@@ -8214,19 +8240,15 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
|
8214
8240
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $59a241f3912e631e$export$2e2bcd8739ae039), {
|
|
8215
8241
|
gene: gene,
|
|
8216
8242
|
study: expr.barStudy,
|
|
8217
|
-
onStudyChange: (v)=>
|
|
8218
|
-
|
|
8219
|
-
patch: {
|
|
8220
|
-
barStudy: v
|
|
8221
|
-
}
|
|
8243
|
+
onStudyChange: (v)=>setExpression({
|
|
8244
|
+
barStudy: v
|
|
8222
8245
|
})
|
|
8223
8246
|
})
|
|
8224
8247
|
}, "bar")
|
|
8225
8248
|
]
|
|
8226
8249
|
});
|
|
8227
8250
|
};
|
|
8228
|
-
var $9e29a4f60318db7a$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectConfiguration', 'selectGrameneParalogs', 'selectExpressionStudies', 'selectUiViewState', 'doRequestParalogs', 'doFetchExpressionStudies', 'doSetExpressionState',
|
|
8229
|
-
$9e29a4f60318db7a$var$Detail);
|
|
8251
|
+
var $9e29a4f60318db7a$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectConfiguration', 'selectGrameneParalogs', 'selectExpressionStudies', 'selectUiViewState', 'doRequestParalogs', 'doFetchExpressionStudies', 'doSetExpressionState', $9e29a4f60318db7a$var$Detail);
|
|
8230
8252
|
|
|
8231
8253
|
|
|
8232
8254
|
|
|
@@ -11589,6 +11611,64 @@ function $f49aa8a0cf25aef4$export$30e3f6c97d3f3a29(ensemblRest) {
|
|
|
11589
11611
|
}
|
|
11590
11612
|
|
|
11591
11613
|
|
|
11614
|
+
/**
|
|
11615
|
+
* Allele frequencies for the primer designer's variant picker.
|
|
11616
|
+
*
|
|
11617
|
+
* A variant listing from `/primers` says which alleles exist but not how common
|
|
11618
|
+
* they are, and how common an allele is decides whether a variant is worth
|
|
11619
|
+
* genotyping at all: a marker near fixation in the panel you mean to screen
|
|
11620
|
+
* tells you nothing. Ensembl REST's `variation` endpoint carries the figures,
|
|
11621
|
+
* and its POST form answers about many ids in one request — ids beyond a couple
|
|
11622
|
+
* of hundred are the difference between one call and hundreds.
|
|
11623
|
+
*
|
|
11624
|
+
* Read from `config.ensemblRest`, which must be the same Ensembl release the
|
|
11625
|
+
* primers API reads variants from (release 115 for sorghum_v11), for the same
|
|
11626
|
+
* reason the gene track and the sequence must: a frequency read against another
|
|
11627
|
+
* release could be attached to the wrong variant entirely.
|
|
11628
|
+
*
|
|
11629
|
+
* Two things about the upstream shape are dealt with here. The tally is spelled
|
|
11630
|
+
* `allele_count`, which is mapped to `count` so a host with some other
|
|
11631
|
+
* frequency source is not made to speak Ensembl; and rows arrive repeated, the
|
|
11632
|
+
* same population and allele several times over, which the designer collapses.
|
|
11633
|
+
*/ /** Ids per request. The designer sends batches; this is a guard, not the policy. */ const $24c27aa5d34bb08e$var$MAX_IDS = 500;
|
|
11634
|
+
function $24c27aa5d34bb08e$export$849a7b5b35ad4649(ensemblRest) {
|
|
11635
|
+
if (!ensemblRest) return undefined;
|
|
11636
|
+
return async ({ system_name: system_name, ids: ids }, options)=>{
|
|
11637
|
+
if (!system_name || !Array.isArray(ids) || !ids.length) return {};
|
|
11638
|
+
const signal = options && options.signal;
|
|
11639
|
+
const wanted = ids.filter((id)=>typeof id === 'string' && id).slice(0, $24c27aa5d34bb08e$var$MAX_IDS);
|
|
11640
|
+
if (!wanted.length) return {};
|
|
11641
|
+
const res = await fetch(`${ensemblRest}/variation/${encodeURIComponent(system_name)}?pops=1`, {
|
|
11642
|
+
method: 'POST',
|
|
11643
|
+
headers: {
|
|
11644
|
+
'Content-Type': 'application/json',
|
|
11645
|
+
Accept: 'application/json'
|
|
11646
|
+
},
|
|
11647
|
+
body: JSON.stringify({
|
|
11648
|
+
ids: wanted
|
|
11649
|
+
}),
|
|
11650
|
+
signal: signal
|
|
11651
|
+
});
|
|
11652
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
11653
|
+
const body = await res.json();
|
|
11654
|
+
if (!body || typeof body !== 'object') return {};
|
|
11655
|
+
const out = {};
|
|
11656
|
+
for (const id of wanted){
|
|
11657
|
+
const rows = body[id] && body[id].populations;
|
|
11658
|
+
if (!Array.isArray(rows)) continue;
|
|
11659
|
+
out[id] = rows.filter((r)=>r && typeof r.population === 'string' && typeof r.allele === 'string' && Number.isFinite(Number(r.frequency))).map((r)=>({
|
|
11660
|
+
population: r.population,
|
|
11661
|
+
allele: r.allele,
|
|
11662
|
+
frequency: Number(r.frequency),
|
|
11663
|
+
// Absent is not nought: a missing tally must stay unknown.
|
|
11664
|
+
count: r.allele_count === undefined || r.allele_count === null ? null : Number(r.allele_count)
|
|
11665
|
+
}));
|
|
11666
|
+
}
|
|
11667
|
+
return out;
|
|
11668
|
+
};
|
|
11669
|
+
}
|
|
11670
|
+
|
|
11671
|
+
|
|
11592
11672
|
// Primers detail: PCR/qPCR primer design, genome specificity and pan-genome
|
|
11593
11673
|
// coverage checks (gramene-primers <PrimerDesigner>, backed by the
|
|
11594
11674
|
// gramene-swagger /primers endpoints).
|
|
@@ -11632,6 +11712,9 @@ const $88d24ae9beb83d5d$var$Primers = (props)=>{
|
|
|
11632
11712
|
const sequenceForRegion = (0, $gXNCa$react.useMemo)(()=>(0, $f49aa8a0cf25aef4$export$30e3f6c97d3f3a29)(ensemblRest), [
|
|
11633
11713
|
ensemblRest
|
|
11634
11714
|
]);
|
|
11715
|
+
const alleleFrequencies = (0, $gXNCa$react.useMemo)(()=>(0, $24c27aa5d34bb08e$export$849a7b5b35ad4649)(ensemblRest), [
|
|
11716
|
+
ensemblRest
|
|
11717
|
+
]);
|
|
11635
11718
|
const byGene = props.uiViewState && props.uiViewState.byGene && props.uiViewState.byGene[geneId];
|
|
11636
11719
|
const saved = byGene && byGene.primers;
|
|
11637
11720
|
const { doSetPrimersState: doSetPrimersState } = props;
|
|
@@ -11654,6 +11737,7 @@ const $88d24ae9beb83d5d$var$Primers = (props)=>{
|
|
|
11654
11737
|
modes: $88d24ae9beb83d5d$var$MODES,
|
|
11655
11738
|
genesInRegion: genesInRegion,
|
|
11656
11739
|
sequenceForRegion: sequenceForRegion,
|
|
11740
|
+
alleleFrequencies: alleleFrequencies,
|
|
11657
11741
|
defaultMode: "gene",
|
|
11658
11742
|
// an empty slice (never visited, or a snapshot without primers) leaves
|
|
11659
11743
|
// the designer uncontrolled until its first change is stored
|