TSUMUGI 1.0.1__py3-none-any.whl
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.
- TSUMUGI/annotator.py +103 -0
- TSUMUGI/argparser.py +599 -0
- TSUMUGI/core.py +185 -0
- TSUMUGI/data/impc_phenodigm.csv +3406 -0
- TSUMUGI/data/mp.obo +143993 -0
- TSUMUGI/filterer.py +36 -0
- TSUMUGI/formatter.py +122 -0
- TSUMUGI/genewise_annotation_builder.py +94 -0
- TSUMUGI/io_handler.py +189 -0
- TSUMUGI/main.py +300 -0
- TSUMUGI/network_constructor.py +603 -0
- TSUMUGI/ontology_handler.py +62 -0
- TSUMUGI/pairwise_similarity_builder.py +66 -0
- TSUMUGI/report_generator.py +122 -0
- TSUMUGI/similarity_calculator.py +498 -0
- TSUMUGI/subcommands/count_filterer.py +47 -0
- TSUMUGI/subcommands/genes_filterer.py +89 -0
- TSUMUGI/subcommands/graphml_builder.py +158 -0
- TSUMUGI/subcommands/life_stage_filterer.py +48 -0
- TSUMUGI/subcommands/mp_filterer.py +142 -0
- TSUMUGI/subcommands/score_filterer.py +22 -0
- TSUMUGI/subcommands/sex_filterer.py +48 -0
- TSUMUGI/subcommands/webapp_builder.py +358 -0
- TSUMUGI/subcommands/zygosity_filterer.py +48 -0
- TSUMUGI/validator.py +65 -0
- TSUMUGI/web/app/css/app.css +1129 -0
- TSUMUGI/web/app/genelist/network_genelist.html +339 -0
- TSUMUGI/web/app/genelist/network_genelist.js +421 -0
- TSUMUGI/web/app/js/data/dataLoader.js +41 -0
- TSUMUGI/web/app/js/export/graphExporter.js +214 -0
- TSUMUGI/web/app/js/graph/centrality.js +495 -0
- TSUMUGI/web/app/js/graph/components.js +30 -0
- TSUMUGI/web/app/js/graph/filters.js +158 -0
- TSUMUGI/web/app/js/graph/highlighter.js +52 -0
- TSUMUGI/web/app/js/graph/layoutController.js +454 -0
- TSUMUGI/web/app/js/graph/valueScaler.js +43 -0
- TSUMUGI/web/app/js/search/geneSearcher.js +93 -0
- TSUMUGI/web/app/js/search/phenotypeSearcher.js +292 -0
- TSUMUGI/web/app/js/ui/dynamicFontSize.js +30 -0
- TSUMUGI/web/app/js/ui/mobilePanel.js +77 -0
- TSUMUGI/web/app/js/ui/slider.js +22 -0
- TSUMUGI/web/app/js/ui/tooltips.js +514 -0
- TSUMUGI/web/app/js/viewer/pageSetup.js +217 -0
- TSUMUGI/web/app/viewer.html +515 -0
- TSUMUGI/web/app/viewer.js +1593 -0
- TSUMUGI/web/css/sanitize.css +363 -0
- TSUMUGI/web/css/top.css +391 -0
- TSUMUGI/web/image/tsumugi-favicon.ico +0 -0
- TSUMUGI/web/image/tsumugi-icon.png +0 -0
- TSUMUGI/web/image/tsumugi-logo.png +0 -0
- TSUMUGI/web/image/tsumugi-logo.svg +69 -0
- TSUMUGI/web/js/genelist_formatter.js +123 -0
- TSUMUGI/web/js/top.js +338 -0
- TSUMUGI/web/open_webapp_linux.sh +25 -0
- TSUMUGI/web/open_webapp_mac.command +25 -0
- TSUMUGI/web/open_webapp_windows.bat +37 -0
- TSUMUGI/web/serve_index.py +110 -0
- TSUMUGI/web/template/template_index.html +197 -0
- TSUMUGI/web_deployer.py +150 -0
- tsumugi-1.0.1.dist-info/METADATA +504 -0
- tsumugi-1.0.1.dist-info/RECORD +64 -0
- tsumugi-1.0.1.dist-info/WHEEL +4 -0
- tsumugi-1.0.1.dist-info/entry_points.txt +3 -0
- tsumugi-1.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { loadJSONGz } from "../data/dataLoader.js";
|
|
2
|
+
|
|
3
|
+
export function getPageConfig() {
|
|
4
|
+
const params = new URLSearchParams(window.location.search);
|
|
5
|
+
const modeParam = params.get("mode");
|
|
6
|
+
const mode = ["phenotype", "genesymbol", "genelist"].includes(modeParam || "") ? modeParam : "phenotype";
|
|
7
|
+
const providedName = params.get("name") || "";
|
|
8
|
+
const name = mode === "genelist" && !providedName ? "geneList" : providedName;
|
|
9
|
+
const title = params.get("title") || name;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
mode,
|
|
13
|
+
name,
|
|
14
|
+
displayName: title || name || "TSUMUGI",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function hidePhenotypeOnlySections(isPhenotypePage) {
|
|
19
|
+
document.querySelectorAll(".phenotype-only").forEach((el) => {
|
|
20
|
+
el.style.display = isPhenotypePage ? "" : "none";
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isBinaryPhenotypeElements(elements) {
|
|
25
|
+
const nodeElements = elements.filter((ele) => ele.data && ele.data.node_color !== undefined);
|
|
26
|
+
if (!nodeElements.length) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const hideSeverityFlags = nodeElements
|
|
31
|
+
.map((ele) => ele.data.hide_severity)
|
|
32
|
+
.filter((value) => value !== undefined);
|
|
33
|
+
if (hideSeverityFlags.length && hideSeverityFlags.every(Boolean)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const uniqueColors = [...new Set(nodeElements.map((ele) => ele.data.node_color).filter((v) => v !== undefined))];
|
|
38
|
+
if (uniqueColors.length === 1) {
|
|
39
|
+
const normalized = String(Math.round(Number(uniqueColors[0])));
|
|
40
|
+
return ["0", "1", "100"].includes(normalized);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function setPageTitle(config, mapSymbolToId, mapPhenotypeToId) {
|
|
47
|
+
const pageTitleLink = document.getElementById("page-title-link");
|
|
48
|
+
const pageTitle = config.displayName || config.name || "TSUMUGI";
|
|
49
|
+
let targetUrl = "";
|
|
50
|
+
|
|
51
|
+
if (config.mode === "phenotype" && mapPhenotypeToId) {
|
|
52
|
+
const phenotypeId = mapPhenotypeToId[config.name];
|
|
53
|
+
if (phenotypeId) {
|
|
54
|
+
targetUrl = `https://www.mousephenotype.org/data/phenotypes/${phenotypeId}`;
|
|
55
|
+
}
|
|
56
|
+
} else if (config.mode === "genesymbol" && mapSymbolToId) {
|
|
57
|
+
const accession = mapSymbolToId[config.name];
|
|
58
|
+
if (accession) {
|
|
59
|
+
targetUrl = `https://www.mousephenotype.org/data/genes/${accession}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (targetUrl) {
|
|
64
|
+
pageTitleLink.href = targetUrl;
|
|
65
|
+
pageTitleLink.target = "_blank";
|
|
66
|
+
pageTitleLink.rel = "noreferrer";
|
|
67
|
+
pageTitleLink.style.pointerEvents = "";
|
|
68
|
+
pageTitleLink.style.cursor = "";
|
|
69
|
+
} else {
|
|
70
|
+
pageTitleLink.removeAttribute("href");
|
|
71
|
+
pageTitleLink.style.pointerEvents = "none";
|
|
72
|
+
pageTitleLink.style.cursor = "default";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pageTitleLink.textContent = pageTitle;
|
|
76
|
+
document.title = `${pageTitle} | TSUMUGI`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function fetchText(path) {
|
|
80
|
+
let text = "";
|
|
81
|
+
try {
|
|
82
|
+
const response = await fetch(path, { cache: "no-cache" });
|
|
83
|
+
if (response.ok || response.status === 0) {
|
|
84
|
+
text = (await response.text()).trim();
|
|
85
|
+
if (text) {
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// fall through to the XHR fallback
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
try {
|
|
95
|
+
const xhr = new XMLHttpRequest();
|
|
96
|
+
xhr.open("GET", path, true);
|
|
97
|
+
xhr.onload = () => {
|
|
98
|
+
if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
|
|
99
|
+
resolve(xhr.responseText.trim());
|
|
100
|
+
} else {
|
|
101
|
+
resolve("");
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
xhr.onerror = () => resolve("");
|
|
105
|
+
xhr.send();
|
|
106
|
+
} catch (e) {
|
|
107
|
+
resolve("");
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function setVersionLabel() {
|
|
113
|
+
const versionLabel = document.getElementById("tsumugi-version");
|
|
114
|
+
if (!versionLabel) return;
|
|
115
|
+
|
|
116
|
+
const candidates = ["../version.txt", "./version.txt"];
|
|
117
|
+
let versionText = "";
|
|
118
|
+
|
|
119
|
+
for (const path of candidates) {
|
|
120
|
+
versionText = await fetchText(path);
|
|
121
|
+
if (versionText) break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
versionLabel.textContent = versionText || "-";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function loadElementsForConfig(config) {
|
|
128
|
+
if (config.mode === "phenotype") {
|
|
129
|
+
return loadJSONGz(`../data/phenotype/${config.name}.json.gz`) || [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (config.mode === "genesymbol") {
|
|
133
|
+
return loadJSONGz(`../data/genesymbol/${config.name}.json.gz`) || [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Gene list page pulls data from localStorage
|
|
137
|
+
try {
|
|
138
|
+
const stored = localStorage.getItem("elements");
|
|
139
|
+
return stored ? JSON.parse(stored) : [];
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error("Failed to parse stored elements for gene list:", error);
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function renderEmptyState(message) {
|
|
147
|
+
const container = document.querySelector(".cy");
|
|
148
|
+
if (!container) return;
|
|
149
|
+
|
|
150
|
+
container.innerHTML = `<div style="padding: 24px; font-size: 16px;">${message}</div>`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function applyNodeMinMax(elements, nodeColorMin, nodeColorMax) {
|
|
154
|
+
// Ensure at least one gene pair remains visible even at slider extremes. Issue #72
|
|
155
|
+
const nodeColorMap = new Map();
|
|
156
|
+
elements.forEach((ele) => {
|
|
157
|
+
if (ele.data.node_color !== undefined && ele.data.id !== undefined) {
|
|
158
|
+
nodeColorMap.set(ele.data.id, ele.data.node_color);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const sortedNodeColors = [...new Set([...nodeColorMap.values()])].sort((a, b) => a - b);
|
|
163
|
+
if (sortedNodeColors.length === 0) {
|
|
164
|
+
return { nodeMin: nodeColorMin, nodeMax: nodeColorMax };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const nodeColorToRank = new Map();
|
|
168
|
+
sortedNodeColors.forEach((val, idx) => {
|
|
169
|
+
nodeColorToRank.set(val, idx + 1);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const edgeRankPairs = [];
|
|
173
|
+
elements.forEach((ele) => {
|
|
174
|
+
if (ele.data.source && ele.data.target) {
|
|
175
|
+
const sourceVal = nodeColorMap.get(ele.data.source);
|
|
176
|
+
const targetVal = nodeColorMap.get(ele.data.target);
|
|
177
|
+
|
|
178
|
+
if (sourceVal !== undefined && targetVal !== undefined) {
|
|
179
|
+
const sourceRank = nodeColorToRank.get(sourceVal);
|
|
180
|
+
const targetRank = nodeColorToRank.get(targetVal);
|
|
181
|
+
const rankSum = sourceRank + targetRank;
|
|
182
|
+
|
|
183
|
+
edgeRankPairs.push({
|
|
184
|
+
rankSum: rankSum,
|
|
185
|
+
minVal: Math.min(sourceVal, targetVal),
|
|
186
|
+
maxVal: Math.max(sourceVal, targetVal),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (edgeRankPairs.length === 0) {
|
|
193
|
+
return { nodeMin: nodeColorMin, nodeMax: nodeColorMax };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const minRankEdge = edgeRankPairs.reduce((a, b) => (a.rankSum < b.rankSum ? a : b));
|
|
197
|
+
const maxRankEdge = edgeRankPairs.reduce((a, b) => (a.rankSum > b.rankSum ? a : b));
|
|
198
|
+
|
|
199
|
+
const nodeMin = minRankEdge.maxVal;
|
|
200
|
+
const nodeMax = maxRankEdge.minVal;
|
|
201
|
+
|
|
202
|
+
elements.forEach((ele) => {
|
|
203
|
+
if (ele.data.node_color !== undefined) {
|
|
204
|
+
ele.data.original_node_color = ele.data.node_color;
|
|
205
|
+
|
|
206
|
+
if (ele.data.node_color <= nodeMin) {
|
|
207
|
+
ele.data.node_color_for_filter = nodeMin;
|
|
208
|
+
} else if (ele.data.node_color >= nodeMax) {
|
|
209
|
+
ele.data.node_color_for_filter = nodeMax;
|
|
210
|
+
} else {
|
|
211
|
+
ele.data.node_color_for_filter = ele.data.node_color;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return { nodeMin, nodeMax };
|
|
217
|
+
}
|