Skip to content

Commit 9c9ac2c

Browse files
committed
SLKP continued refactoring up to 09302024
1 parent b72f7f3 commit 9c9ac2c

8 files changed

+2862
-2491
lines changed

src/clusternetwork.js

+252-2,402
Large diffs are not rendered by default.

src/clustersOfInterest.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ function draw_priority_set_table(self, container, priority_groups) {
12741274
{
12751275
value: [
12761276
pg.node_objects.length,
1277-
_.filter(pg.nodes, (g) => self.priority_groups_is_new_node(pg, g))
1277+
_.filter(pg.nodes, (g) => self.priority_groups_is_new_node(g))
12781278
.length,
12791279
pg.createdBy === kGlobals.CDCCOICreatedBySystem && pg.pending,
12801280
pg.meets_priority_def,

src/column_definitions.js

+382
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
/**
2+
A collection of table column definitions
3+
*/
4+
5+
var d3 = require("d3"),
6+
_ = require("underscore"),
7+
clustersOfInterest = require("./clustersOfInterest.js"),
8+
HTX = require("./hiv_tx_network.js");
9+
10+
/**
11+
Column definitions for rendered tables
12+
Each column definition is object-based and has the following components
13+
14+
description: [this relates to the heading]
15+
value (text): the displayed name of the column
16+
sort (function): a function which takes the value associated with the table cell, and returns a value upon which to sort the column
17+
help (text): the text to display in a popover when the user hovers over the column name
18+
generator:
19+
a function that returns a data-driven definition of a cell
20+
it takes as an argument the value that is associated with a cell
21+
and returns an object with the following values
22+
23+
html (bool): whether or not the returned value should be rendered as HTML (default is no, i.e. text)
24+
value: what is the value associated with the cell
25+
volatile (bool): if set, this cell will be re-rendered under operations which could modify how its displayed (e.g. is a CoI editor open)
26+
format (function): how to render the cell value
27+
actions (function): generate context-specific menus for the cell
28+
returns null (none) or a vector (definitions of actions)
29+
30+
An action is an object with the following fields
31+
icon (text): use this font awesome icon
32+
action (function): a function that takes the clicked button and the cell value and does something
33+
help (text): the help message to display on hover over the button
34+
35+
36+
*/
37+
38+
/**
39+
Secure HIV-TRACE subcluster table columns
40+
*/
41+
function secure_hiv_trace_subcluster_columns(self) {
42+
return [
43+
/** definition for the column which shows the #of cases dx'ed within 36 months
44+
the value is an array, which enumerates the number of connected components of the 0.5% subcluster, which are ALL within 36 month dx,
45+
so can be more than one.
46+
47+
The only action is to add the nodes in this subcluster to a CoI editor if open
48+
49+
Accepts a _self_ argument for transitive closure
50+
51+
*/
52+
{
53+
description: {
54+
value: "Cases dx within 36 months",
55+
sort: function (c) {
56+
return c.value.length ? c.value[0].length : 0;
57+
},
58+
help: "Number of cases diagnosed in the past 36 months connected only through cases diagnosed within the past 36 months",
59+
},
60+
generator: function (cluster) {
61+
return {
62+
html: true,
63+
value: cluster.recent_nodes,
64+
volatile: true,
65+
format: function (v) {
66+
v = v || [];
67+
if (v.length) {
68+
return _.map(v, (e) => e.length).join(", ");
69+
}
70+
return "";
71+
},
72+
actions: function (item, value) {
73+
if (
74+
!clustersOfInterest.get_editor() ||
75+
cluster.recent_nodes.length === 0
76+
) {
77+
return null;
78+
}
79+
return _.map(cluster.recent_nodes, (c) => {
80+
const nodeset = new Set(c);
81+
return {
82+
icon: "fa-plus",
83+
action: function (button, v) {
84+
if (clustersOfInterest.get_editor()) {
85+
clustersOfInterest
86+
.get_editor()
87+
.append_node_objects(
88+
_.filter(
89+
cluster.children,
90+
(n) => nodeset.has(n.id) && n.priority_flag > 0
91+
)
92+
);
93+
}
94+
return false;
95+
},
96+
help: "Add to cluster of interest",
97+
};
98+
});
99+
},
100+
};
101+
},
102+
},
103+
104+
/** definition for the column which shows the #of cases dx'ed within 12 months
105+
the value is an array, which enumerates the number of connected components of the 0.5% subcluster, which connect through nodes dx'ed 36 month dx, so can be more than one.
106+
107+
The actions are to add the nodes in this subcluster to a CoI editor if open, and to determine if the nodes in this set are already a part of the CoI.
108+
109+
*/
110+
111+
{
112+
description: {
113+
value: "Cases dx within 12 months",
114+
//"value",
115+
sort: function (c) {
116+
const v = c.value || [];
117+
return v.length > 0 ? v[0].length : 0;
118+
},
119+
presort: "desc",
120+
help: "Number of cases diagnosed in the past 12 months connected only through cases diagnosed within the past 36 months",
121+
},
122+
generator: function (cluster) {
123+
const definition = {
124+
html: true,
125+
value: cluster.priority_score,
126+
volatile: true,
127+
format: function (v) {
128+
v = v || [];
129+
if (v.length) {
130+
var str = _.map(v, (c) => c.length).join(", ");
131+
if (
132+
v[0].length >= self.CDC_data["autocreate-priority-set-size"]
133+
) {
134+
var color = "red";
135+
return "<span style='color:" + color + "'>" + str + "</span>";
136+
}
137+
return str;
138+
}
139+
return "";
140+
},
141+
};
142+
143+
definition["actions"] = function (item, value) {
144+
let result = [];
145+
146+
if (cluster.priority_score.length > 0) {
147+
result = result.concat(
148+
_.map(cluster.priority_score, (c) => ({
149+
icon: "fa-question",
150+
help:
151+
"Do some of these " +
152+
c.length +
153+
" nodes belong to a cluster of interest?",
154+
action: function (this_button, cv) {
155+
const nodeset = new Set(c);
156+
this_button = $(this_button.node());
157+
if (this_button.data("popover_shown") !== "shown") {
158+
const popover = this_button
159+
.popover({
160+
sanitize: false,
161+
placement: "right",
162+
container: "body",
163+
html: true,
164+
content: HTX.HIVTxNetwork.lookup_form_generator,
165+
trigger: "manual",
166+
})
167+
.on("shown.bs.popover", function (e) {
168+
var clicked_object = d3.select(this);
169+
var popover_div = d3.select(
170+
"#" + clicked_object.attr("aria-describedby")
171+
);
172+
var list_element = popover_div.selectAll(
173+
self.get_ui_element_selector_by_role(
174+
"priority-membership-list",
175+
true
176+
)
177+
);
178+
179+
list_element.selectAll("li").remove();
180+
let check_membership = _.filter(
181+
_.map(self.defined_priority_groups, (g) =>
182+
//console.log(g);
183+
[
184+
g.name,
185+
_.filter(g.nodes, (n) => nodeset.has(n.name))
186+
.length,
187+
_.filter(
188+
g.partitioned_nodes[1]["new_direct"],
189+
(n) => nodeset.has(n.id)
190+
).length,
191+
_.filter(
192+
g.partitioned_nodes[1]["new_indirect"],
193+
(n) => nodeset.has(n.id)
194+
).length,
195+
]
196+
),
197+
(gg) => gg[1] + gg[2] + gg[3] > 0
198+
);
199+
200+
if (check_membership.length === 0) {
201+
check_membership = [
202+
[
203+
"No nodes belong to any cluster of interest or are linked to any of the clusters of interest.",
204+
],
205+
];
206+
} else {
207+
check_membership = _.map(check_membership, (m) => {
208+
let description = "";
209+
if (m[1]) {
210+
description += " " + m[1] + " nodes belong";
211+
}
212+
if (m[2]) {
213+
description +=
214+
(description.length ? ", " : " ") +
215+
m[2] +
216+
" nodes are directly linked @ " +
217+
kGlobals.formats.PercentFormatShort(
218+
self.subcluster_threshold
219+
);
220+
}
221+
if (m[3]) {
222+
description +=
223+
(description.length ? ", " : " ") +
224+
m[3] +
225+
" nodes are indirectly linked @ " +
226+
kGlobals.formats.PercentFormatShort(
227+
self.subcluster_threshold
228+
);
229+
}
230+
231+
description +=
232+
" to cluster of interest <code>" +
233+
m[0] +
234+
"</code>";
235+
return description;
236+
});
237+
}
238+
list_element = list_element
239+
.selectAll("li")
240+
.data(check_membership);
241+
list_element.enter().insert("li");
242+
list_element.html((d) => d);
243+
});
244+
245+
popover.popover("show");
246+
this_button.data("popover_shown", "shown");
247+
this_button
248+
.off("hidden.bs.popover")
249+
.on("hidden.bs.popover", function () {
250+
$(this).data("popover_shown", "hidden");
251+
});
252+
} else {
253+
this_button.data("popover_shown", "hidden");
254+
this_button.popover("destroy");
255+
}
256+
},
257+
}))
258+
);
259+
}
260+
261+
if (
262+
clustersOfInterest.get_editor() &&
263+
cluster.priority_score.length > 0
264+
) {
265+
result = result.concat(
266+
_.map(cluster.priority_score, (c) => {
267+
const nodeset = new Set(c);
268+
return {
269+
icon: "fa-plus",
270+
action: function (button, v) {
271+
if (clustersOfInterest.get_editor()) {
272+
clustersOfInterest
273+
.get_editor()
274+
.append_node_objects(
275+
_.filter(
276+
cluster.children,
277+
(n) =>
278+
nodeset.has(n.id) &&
279+
(n.priority_flag === 2 || n.priority_flag === 1)
280+
)
281+
);
282+
}
283+
return false;
284+
},
285+
help: "Add to cluster of interest",
286+
};
287+
})
288+
);
289+
}
290+
291+
return result;
292+
};
293+
294+
return definition;
295+
},
296+
},
297+
];
298+
}
299+
300+
/**
301+
Secure HIV-TRACE node table columns
302+
SLKP: note that this seems to be fully deprecated.
303+
*/
304+
305+
function secure_hiv_trace_node_columns(self) {
306+
return [
307+
{
308+
description: {
309+
value: "Recent and Rapid",
310+
sort: "value",
311+
help: "Is the node a member of a regular or recent & rapid subcluster?",
312+
},
313+
generator: function (node) {
314+
return {
315+
callback: function (element, payload) {
316+
//payload = _.filter (payload, function (d) {return d});
317+
var this_cell = d3.select(element);
318+
319+
var data_to_use = [
320+
[payload[0][0], payload[0][1], payload[0][2]],
321+
[payload[1][0] ? "36 months" : "", payload[1][1]],
322+
[payload[2][0] ? "12 months" : "", payload[2][1]],
323+
[
324+
payload.length > 3 && payload[3][0]
325+
? "Recent cluster >= 3"
326+
: "",
327+
payload.length > 3 ? payload[3][1] : null,
328+
],
329+
];
330+
331+
this_cell.selectAll("span").remove();
332+
333+
_.each(data_to_use, (button_text) => {
334+
//self.open_exclusive_tab_view (cluster_id)
335+
if (button_text[0].length) {
336+
var button_obj = this_cell
337+
.append("span")
338+
.classed("btn btn-xs btn-node-property", true)
339+
.classed(button_text[1], true)
340+
.text(button_text[0]);
341+
342+
if (_.isFunction(button_text[2])) {
343+
button_obj.on("click", button_text[2]);
344+
} else {
345+
button_obj.attr("disabled", true);
346+
}
347+
}
348+
});
349+
},
350+
value: function () {
351+
return [
352+
[
353+
node.subcluster_label
354+
? "Subcluster " + node.subcluster_label
355+
: "",
356+
"btn-primary",
357+
node.subcluster_label
358+
? function () {
359+
self.view_subcluster(
360+
node.subcluster_label,
361+
(n) => n.subcluster_label === node.subcluster_label,
362+
"Subcluster " + node.subcluster_label
363+
);
364+
}
365+
: null,
366+
],
367+
368+
[node.priority_flag === 3, "btn-warning"],
369+
[node.priority_flag === 1, "btn-danger"],
370+
[node.priority_flag === 2, "btn-danger"],
371+
];
372+
},
373+
};
374+
},
375+
},
376+
];
377+
}
378+
379+
module.exports = {
380+
secure_hiv_trace_subcluster_columns,
381+
secure_hiv_trace_node_columns,
382+
};

0 commit comments

Comments
 (0)