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.
Files changed (64) hide show
  1. TSUMUGI/annotator.py +103 -0
  2. TSUMUGI/argparser.py +599 -0
  3. TSUMUGI/core.py +185 -0
  4. TSUMUGI/data/impc_phenodigm.csv +3406 -0
  5. TSUMUGI/data/mp.obo +143993 -0
  6. TSUMUGI/filterer.py +36 -0
  7. TSUMUGI/formatter.py +122 -0
  8. TSUMUGI/genewise_annotation_builder.py +94 -0
  9. TSUMUGI/io_handler.py +189 -0
  10. TSUMUGI/main.py +300 -0
  11. TSUMUGI/network_constructor.py +603 -0
  12. TSUMUGI/ontology_handler.py +62 -0
  13. TSUMUGI/pairwise_similarity_builder.py +66 -0
  14. TSUMUGI/report_generator.py +122 -0
  15. TSUMUGI/similarity_calculator.py +498 -0
  16. TSUMUGI/subcommands/count_filterer.py +47 -0
  17. TSUMUGI/subcommands/genes_filterer.py +89 -0
  18. TSUMUGI/subcommands/graphml_builder.py +158 -0
  19. TSUMUGI/subcommands/life_stage_filterer.py +48 -0
  20. TSUMUGI/subcommands/mp_filterer.py +142 -0
  21. TSUMUGI/subcommands/score_filterer.py +22 -0
  22. TSUMUGI/subcommands/sex_filterer.py +48 -0
  23. TSUMUGI/subcommands/webapp_builder.py +358 -0
  24. TSUMUGI/subcommands/zygosity_filterer.py +48 -0
  25. TSUMUGI/validator.py +65 -0
  26. TSUMUGI/web/app/css/app.css +1129 -0
  27. TSUMUGI/web/app/genelist/network_genelist.html +339 -0
  28. TSUMUGI/web/app/genelist/network_genelist.js +421 -0
  29. TSUMUGI/web/app/js/data/dataLoader.js +41 -0
  30. TSUMUGI/web/app/js/export/graphExporter.js +214 -0
  31. TSUMUGI/web/app/js/graph/centrality.js +495 -0
  32. TSUMUGI/web/app/js/graph/components.js +30 -0
  33. TSUMUGI/web/app/js/graph/filters.js +158 -0
  34. TSUMUGI/web/app/js/graph/highlighter.js +52 -0
  35. TSUMUGI/web/app/js/graph/layoutController.js +454 -0
  36. TSUMUGI/web/app/js/graph/valueScaler.js +43 -0
  37. TSUMUGI/web/app/js/search/geneSearcher.js +93 -0
  38. TSUMUGI/web/app/js/search/phenotypeSearcher.js +292 -0
  39. TSUMUGI/web/app/js/ui/dynamicFontSize.js +30 -0
  40. TSUMUGI/web/app/js/ui/mobilePanel.js +77 -0
  41. TSUMUGI/web/app/js/ui/slider.js +22 -0
  42. TSUMUGI/web/app/js/ui/tooltips.js +514 -0
  43. TSUMUGI/web/app/js/viewer/pageSetup.js +217 -0
  44. TSUMUGI/web/app/viewer.html +515 -0
  45. TSUMUGI/web/app/viewer.js +1593 -0
  46. TSUMUGI/web/css/sanitize.css +363 -0
  47. TSUMUGI/web/css/top.css +391 -0
  48. TSUMUGI/web/image/tsumugi-favicon.ico +0 -0
  49. TSUMUGI/web/image/tsumugi-icon.png +0 -0
  50. TSUMUGI/web/image/tsumugi-logo.png +0 -0
  51. TSUMUGI/web/image/tsumugi-logo.svg +69 -0
  52. TSUMUGI/web/js/genelist_formatter.js +123 -0
  53. TSUMUGI/web/js/top.js +338 -0
  54. TSUMUGI/web/open_webapp_linux.sh +25 -0
  55. TSUMUGI/web/open_webapp_mac.command +25 -0
  56. TSUMUGI/web/open_webapp_windows.bat +37 -0
  57. TSUMUGI/web/serve_index.py +110 -0
  58. TSUMUGI/web/template/template_index.html +197 -0
  59. TSUMUGI/web_deployer.py +150 -0
  60. tsumugi-1.0.1.dist-info/METADATA +504 -0
  61. tsumugi-1.0.1.dist-info/RECORD +64 -0
  62. tsumugi-1.0.1.dist-info/WHEEL +4 -0
  63. tsumugi-1.0.1.dist-info/entry_points.txt +3 -0
  64. tsumugi-1.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,292 @@
1
+ // ############################################################
2
+ // Phenotype Search and Highlight Functions
3
+ // ############################################################
4
+
5
+ // Hold the available and selected phenotype lists
6
+ let allPhenotypes = [];
7
+ let selectedPhenotypes = new Set();
8
+ let cytoscapeInstance = null;
9
+
10
+ /**
11
+ * Initialize the phenotype search feature.
12
+ * @param {Object} params - Initialization parameters
13
+ * @param {Object} params.cy - Cytoscape instance
14
+ * @param {Array} params.elements - Network elements
15
+ */
16
+ export function setupPhenotypeSearch({ cy, elements }) {
17
+ cytoscapeInstance = cy;
18
+ initializePhenotypeSearch(cy);
19
+ setupPhenotypeSearchInput();
20
+
21
+ // Expose helper for the inline onclick handlers
22
+ window.removeSelectedPhenotype = removeSelectedPhenotype;
23
+
24
+ // Make updatePhenotypeHighlight available to other modules
25
+ window.updatePhenotypeHighlight = () => updatePhenotypeHighlight(cy);
26
+
27
+ // Allow other modules to refresh the phenotype list after filtering
28
+ window.refreshPhenotypeList = () => refreshPhenotypeList();
29
+ }
30
+
31
+ /**
32
+ * Build the phenotype list from the currently visible genes.
33
+ * @param {Object} cy - Cytoscape instance
34
+ */
35
+ function initializePhenotypeSearch(cy) {
36
+ const phenotypeSet = new Set();
37
+
38
+ // Extract phenotypes only from visible nodes
39
+ cy.nodes().forEach((node) => {
40
+ // Skip nodes that are hidden
41
+ if (node.style("display") !== "none" && !node.hidden()) {
42
+ const nodeData = node.data();
43
+ if (nodeData.phenotype) {
44
+ const phenotypes = Array.isArray(nodeData.phenotype) ? nodeData.phenotype : [nodeData.phenotype];
45
+ phenotypes.forEach((phenotype) => {
46
+ if (phenotype && phenotype.trim() !== "") {
47
+ phenotypeSet.add(phenotype.trim());
48
+ }
49
+ });
50
+ }
51
+ }
52
+ });
53
+
54
+ allPhenotypes = Array.from(phenotypeSet).sort();
55
+ }
56
+
57
+ /**
58
+ * Wire up event handlers for the phenotype search input.
59
+ */
60
+ function setupPhenotypeSearchInput() {
61
+ const searchInput = document.getElementById("phenotype-search");
62
+ const suggestionsList = document.getElementById("phenotype-suggestions");
63
+
64
+ if (!searchInput || !suggestionsList) {
65
+ console.warn("Phenotype search elements not found in DOM");
66
+ return;
67
+ }
68
+
69
+ searchInput.addEventListener("input", function () {
70
+ const searchTerm = this.value.toLowerCase().trim();
71
+
72
+ if (searchTerm.length === 0) {
73
+ suggestionsList.hidden = true;
74
+ return;
75
+ }
76
+
77
+ // Filter out phenotypes that are already selected
78
+ const filteredPhenotypes = allPhenotypes.filter(
79
+ (phenotype) => phenotype.toLowerCase().includes(searchTerm) && !selectedPhenotypes.has(phenotype),
80
+ );
81
+
82
+ displayPhenotypeSuggestions(filteredPhenotypes);
83
+ });
84
+
85
+ // Display suggestions when the field is clicked
86
+ searchInput.addEventListener("click", function () {
87
+ const searchTerm = this.value.toLowerCase().trim();
88
+
89
+ if (searchTerm.length === 0) {
90
+ // If empty, show all phenotypes except the selected ones
91
+ const availablePhenotypes = allPhenotypes.filter((phenotype) => !selectedPhenotypes.has(phenotype));
92
+ displayPhenotypeSuggestions(availablePhenotypes);
93
+ } else {
94
+ // Otherwise, show matching phenotypes
95
+ const filteredPhenotypes = allPhenotypes.filter(
96
+ (phenotype) => phenotype.toLowerCase().includes(searchTerm) && !selectedPhenotypes.has(phenotype),
97
+ );
98
+ displayPhenotypeSuggestions(filteredPhenotypes);
99
+ }
100
+ });
101
+
102
+ // Also display suggestions when the field gains focus
103
+ searchInput.addEventListener("focus", function () {
104
+ const searchTerm = this.value.toLowerCase().trim();
105
+
106
+ if (searchTerm.length === 0) {
107
+ // If empty, show all phenotypes except the selected ones
108
+ const availablePhenotypes = allPhenotypes.filter((phenotype) => !selectedPhenotypes.has(phenotype));
109
+ displayPhenotypeSuggestions(availablePhenotypes);
110
+ }
111
+ });
112
+
113
+ // Hide the suggestions when clicking outside the input
114
+ document.addEventListener("click", function (event) {
115
+ if (!searchInput.contains(event.target) && !suggestionsList.contains(event.target)) {
116
+ suggestionsList.hidden = true;
117
+ }
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Render the phenotype suggestions list.
123
+ * @param {Array} phenotypes - Phenotypes to display
124
+ */
125
+ function displayPhenotypeSuggestions(phenotypes) {
126
+ const suggestionsList = document.getElementById("phenotype-suggestions");
127
+ suggestionsList.innerHTML = "";
128
+
129
+ if (phenotypes.length === 0) {
130
+ suggestionsList.hidden = true;
131
+ return;
132
+ }
133
+
134
+ // Use the full list (scroll is possible in the UI)
135
+ const displayPhenotypes = phenotypes;
136
+
137
+ displayPhenotypes.forEach((phenotype) => {
138
+ const li = document.createElement("li");
139
+ li.textContent = phenotype;
140
+ li.addEventListener("click", function () {
141
+ addSelectedPhenotype(phenotype);
142
+ document.getElementById("phenotype-search").value = "";
143
+ suggestionsList.hidden = true;
144
+ });
145
+ suggestionsList.appendChild(li);
146
+ });
147
+
148
+ suggestionsList.hidden = false;
149
+ }
150
+
151
+ /**
152
+ * Add a phenotype to the selection list.
153
+ * @param {string} phenotype - Phenotype to add
154
+ */
155
+ function addSelectedPhenotype(phenotype) {
156
+ if (selectedPhenotypes.has(phenotype)) return;
157
+
158
+ selectedPhenotypes.add(phenotype);
159
+ displaySelectedPhenotypes();
160
+
161
+ // Invoke the globally exposed updatePhenotypeHighlight helper
162
+ if (window.updatePhenotypeHighlight) {
163
+ window.updatePhenotypeHighlight();
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Remove a selected phenotype (called from inline handlers).
169
+ * @param {string} phenotype - Phenotype to remove
170
+ */
171
+ function removeSelectedPhenotype(phenotype) {
172
+ selectedPhenotypes.delete(phenotype);
173
+ displaySelectedPhenotypes();
174
+
175
+ // Invoke the globally exposed updatePhenotypeHighlight helper
176
+ if (window.updatePhenotypeHighlight) {
177
+ window.updatePhenotypeHighlight();
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Render the currently selected phenotypes as tags.
183
+ */
184
+ function displaySelectedPhenotypes() {
185
+ const container = document.getElementById("selected-phenotypes");
186
+ if (!container) return;
187
+
188
+ container.innerHTML = "";
189
+
190
+ selectedPhenotypes.forEach((phenotype) => {
191
+ const tag = document.createElement("div");
192
+ tag.className = "selected-phenotype-tag";
193
+ tag.innerHTML = `
194
+ <span class="phenotype-text">${phenotype}</span>
195
+ <button class="remove-btn" onclick="removeSelectedPhenotype('${phenotype.replace(/'/g, "\\'")}')">&times;</button>
196
+ `;
197
+ container.appendChild(tag);
198
+ });
199
+ }
200
+
201
+ /**
202
+ * Highlight genes that match any of the selected phenotypes.
203
+ * @param {Object} cy - Cytoscape instance
204
+ */
205
+ function updatePhenotypeHighlight(cy) {
206
+ // Reset existing highlights
207
+ cy.nodes().removeClass("phenotype-highlight");
208
+
209
+ if (selectedPhenotypes.size === 0) {
210
+ return; // Nothing to highlight
211
+ }
212
+
213
+ // Highlight genes that match the selected phenotypes
214
+ cy.nodes().forEach((node) => {
215
+ const nodeData = node.data();
216
+
217
+ if (nodeData.phenotype) {
218
+ const nodePhenotypes = Array.isArray(nodeData.phenotype) ? nodeData.phenotype : [nodeData.phenotype];
219
+
220
+ // Look for any overlap between the node's phenotypes and the selected ones
221
+ const hasSelectedPhenotype = Array.from(selectedPhenotypes).some((selectedPhenotype) =>
222
+ nodePhenotypes.some(
223
+ (nodePhenotype) => nodePhenotype && nodePhenotype.trim() === selectedPhenotype.trim(),
224
+ ),
225
+ );
226
+
227
+ if (hasSelectedPhenotype) {
228
+ node.addClass("phenotype-highlight");
229
+ }
230
+ }
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Return the set of currently selected phenotypes.
236
+ * @returns {Set} Selected phenotype set
237
+ */
238
+ export function getSelectedPhenotypes() {
239
+ return new Set(selectedPhenotypes);
240
+ }
241
+
242
+ /**
243
+ * Clear all selected phenotypes.
244
+ */
245
+ export function clearSelectedPhenotypes() {
246
+ selectedPhenotypes.clear();
247
+ displaySelectedPhenotypes();
248
+
249
+ if (window.updatePhenotypeHighlight) {
250
+ window.updatePhenotypeHighlight();
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Rebuild the phenotype list after filters change.
256
+ */
257
+ function refreshPhenotypeList() {
258
+ if (!cytoscapeInstance) return;
259
+
260
+ // Preserve the current search term
261
+ const searchInput = document.getElementById("phenotype-search");
262
+ const currentSearchValue = searchInput ? searchInput.value : "";
263
+
264
+ // Reinitialize the phenotype list
265
+ initializePhenotypeSearch(cytoscapeInstance);
266
+
267
+ // Drop selected phenotypes that no longer exist
268
+ const updatedSelectedPhenotypes = new Set();
269
+ selectedPhenotypes.forEach((phenotype) => {
270
+ if (allPhenotypes.includes(phenotype)) {
271
+ updatedSelectedPhenotypes.add(phenotype);
272
+ }
273
+ });
274
+ selectedPhenotypes = updatedSelectedPhenotypes;
275
+
276
+ // Refresh the UI
277
+ displaySelectedPhenotypes();
278
+
279
+ // Update suggestions if a search term was present
280
+ if (searchInput && currentSearchValue.trim().length > 0) {
281
+ const searchTerm = currentSearchValue.toLowerCase().trim();
282
+ const filteredPhenotypes = allPhenotypes.filter(
283
+ (phenotype) => phenotype.toLowerCase().includes(searchTerm) && !selectedPhenotypes.has(phenotype),
284
+ );
285
+ displayPhenotypeSuggestions(filteredPhenotypes);
286
+ }
287
+
288
+ // Refresh highlights
289
+ if (window.updatePhenotypeHighlight) {
290
+ window.updatePhenotypeHighlight();
291
+ }
292
+ }
@@ -0,0 +1,30 @@
1
+ // ============================================================
2
+ // Dynamic header font sizing
3
+ // ============================================================
4
+
5
+ export function initDynamicFontSize() {
6
+ const adjustHeaderFontSize = () => {
7
+ const header = document.querySelector(".header-container h1");
8
+ if (!header) return;
9
+
10
+ const viewportWidth = window.innerWidth;
11
+
12
+ // Base font size derived from viewport width.
13
+ let baseFontSize = viewportWidth * 0.04;
14
+ baseFontSize = Math.max(14, Math.min(36, baseFontSize));
15
+
16
+ if (viewportWidth <= 600) {
17
+ baseFontSize = Math.min(baseFontSize, viewportWidth * 0.045);
18
+ }
19
+
20
+ header.style.fontSize = `${baseFontSize}px`;
21
+ };
22
+
23
+ adjustHeaderFontSize();
24
+
25
+ let resizeTimer;
26
+ window.addEventListener("resize", () => {
27
+ clearTimeout(resizeTimer);
28
+ resizeTimer = setTimeout(adjustHeaderFontSize, 100);
29
+ });
30
+ }
@@ -0,0 +1,77 @@
1
+ // ============================================================
2
+ // Mobile panel toggles for small screens
3
+ // ============================================================
4
+
5
+ export function initMobilePanel() {
6
+ const menuToggle = document.getElementById("menu-toggle");
7
+ const leftPanel = document.querySelector(".left-control-panel-container");
8
+ const rightPanel = document.querySelector(".right-control-panel-container");
9
+ const closeButton = document.getElementById("close-panel");
10
+
11
+ if (!menuToggle || !leftPanel || !rightPanel || !closeButton) {
12
+ return;
13
+ }
14
+
15
+ // On small screens move the right panel beneath the left panel.
16
+ const reorganizePanels = () => {
17
+ if (window.innerWidth <= 600) {
18
+ if (rightPanel.parentNode !== leftPanel) {
19
+ leftPanel.appendChild(rightPanel);
20
+ }
21
+ } else {
22
+ const bodyContainer = document.querySelector(".body-container");
23
+ if (rightPanel.parentNode === leftPanel && bodyContainer) {
24
+ bodyContainer.appendChild(rightPanel);
25
+ }
26
+ }
27
+ };
28
+
29
+ reorganizePanels();
30
+ window.addEventListener("resize", reorganizePanels);
31
+
32
+ const openPanel = (event) => {
33
+ event.stopPropagation();
34
+ event.preventDefault();
35
+
36
+ menuToggle.style.display = "none";
37
+ leftPanel.classList.add("active");
38
+ if (window.innerWidth <= 600) {
39
+ rightPanel.classList.add("active");
40
+ }
41
+ closeButton.style.display = "block";
42
+ };
43
+
44
+ const closePanel = (event) => {
45
+ if (event) {
46
+ event.stopPropagation();
47
+ event.preventDefault();
48
+ }
49
+
50
+ leftPanel.classList.remove("active");
51
+ rightPanel.classList.remove("active");
52
+ closeButton.style.display = "none";
53
+
54
+ setTimeout(() => {
55
+ menuToggle.style.display = "block";
56
+ }, 50);
57
+ };
58
+
59
+ ["click", "touchstart"].forEach((eventName) => {
60
+ menuToggle.addEventListener(eventName, openPanel);
61
+ });
62
+
63
+ ["click", "touchstart"].forEach((eventName) => {
64
+ closeButton.addEventListener(eventName, closePanel);
65
+ });
66
+
67
+ document.addEventListener("click", (event) => {
68
+ if (
69
+ leftPanel.classList.contains("active") &&
70
+ !leftPanel.contains(event.target) &&
71
+ !menuToggle.contains(event.target) &&
72
+ !closeButton.contains(event.target)
73
+ ) {
74
+ closePanel();
75
+ }
76
+ });
77
+ }
@@ -0,0 +1,22 @@
1
+ // Generic slider factory supporting single-value and range sliders
2
+ export function createSlider(id, start, min, max, step, updateCallback, isRange = false) {
3
+ const slider = document.getElementById(id);
4
+ if (!slider) {
5
+ console.error(`Slider with ID '${id}' not found.`);
6
+ return null;
7
+ }
8
+
9
+ noUiSlider.create(slider, {
10
+ start: isRange ? start : [start], // Use [start, end] when range mode is enabled
11
+ connect: true,
12
+ range: { min: min, max: max },
13
+ step: step,
14
+ });
15
+
16
+ slider.noUiSlider.on("update", function (value) {
17
+ const intValues = isRange ? value.map(Math.round) : Math.round(value);
18
+ updateCallback(intValues);
19
+ });
20
+
21
+ return slider.noUiSlider;
22
+ }