Skip to content

Commit 1db0d3a

Browse files
committed
Editor for nodes, CoI auto-expansion testing
1 parent b44ebc2 commit 1db0d3a

7 files changed

+356
-30
lines changed

html/priority-sets-args.html

+4
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,10 @@ <h5 class="modal-desc">Select CoI from the list</h5>
776776

777777
<div id="trace-nodes" class="tab-pane">
778778
<div class="row">
779+
<div
780+
class="col-lg-12"
781+
data-hivtrace-ui-role="node_search_div"
782+
></div>
779783
<div class="col-lg-12">
780784
<span class="pull-right" id="node-table-export"> </span>
781785
<p class="lead">Linked individuals</p>

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@
2525
"ink-docstrap": "^1.3.2",
2626
"jquery": "^3.6.1",
2727
"js-convert-case": "^4.2.0",
28+
"jQuery-QueryBuilder": "^2.7.0",
2829
"jslint": "^0.12.1",
2930
"jspanel4": "4.16.1",
3031
"latest": "^0.2.0",
3132
"taffydb": "^2.7.3",
3233
"topojson": "3.x",
33-
"underscore": "1.x"
34+
"underscore": "1.x",
35+
"bootstrap-datepicker": "^1.10.x"
3436
},
3537
"devDependencies": {
3638
"@babel/core": "^7.20.2",

src/clusternetwork.js

+262-18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import * as kGlobals from "./globals.js";
1515
import * as network from "./network.js";
1616
import * as HTX from "./hiv_tx_network.js";
1717
import * as columnDefinitions from "./column_definitions.js";
18+
import "jQuery-QueryBuilder";
19+
import "jQuery-QueryBuilder/dist/css/query-builder.default.css";
20+
import "bootstrap-datepicker";
1821

1922
var hivtrace_cluster_network_graph = function (
2023
json,
@@ -73,7 +76,19 @@ var hivtrace_cluster_network_graph = function (
7376
/** SLKP 20190902: somehow some of our networks have malformed edges! This will remove them */
7477
json.Edges = _.filter(json.Edges, (e) => "source" in e && "target" in e);
7578

76-
var self = new HTX.HIVTxNetwork(json, button_bar_ui);
79+
/** Not primary networks are individual cluster/subcluster views.
80+
They don't interfere with the primary network object, and UI elements
81+
82+
*/
83+
84+
const izPrimaryGraph = network.check_network_option(
85+
options,
86+
"secondary",
87+
true,
88+
false
89+
);
90+
91+
var self = new HTX.HIVTxNetwork(json, button_bar_ui, null, !izPrimaryGraph);
7792

7893
self.process_multiple_sequences();
7994

@@ -186,18 +201,6 @@ var hivtrace_cluster_network_graph = function (
186201
null
187202
);
188203

189-
/** Not primary networks are individual cluster/subcluster views.
190-
They don't interfere with the primary network object, and UI elements
191-
192-
*/
193-
194-
self.isPrimaryGraph = network.check_network_option(
195-
options,
196-
"secondary",
197-
true,
198-
false
199-
);
200-
201204
self.parent_graph_object = network.check_network_option(
202205
options,
203206
"parent_graph",
@@ -1894,7 +1897,6 @@ var hivtrace_cluster_network_graph = function (
18941897
} else {
18951898
cluster_nodes = self._extract_nodes_by_id(cluster_id);
18961899
}
1897-
18981900
d3.select(
18991901
self.get_ui_element_selector_by_role("cluster_list_data_export", true)
19001902
).on("click", (d) => {
@@ -3874,7 +3876,7 @@ var hivtrace_cluster_network_graph = function (
38743876

38753877
self.draw_node_table(
38763878
null,
3877-
null,
3879+
node_list,
38783880
[table_headers],
38793881
table_rows,
38803882
container,
@@ -4827,12 +4829,13 @@ var hivtrace_cluster_network_graph = function (
48274829
) {
48284830
self.legend_svg
48294831
.append("g")
4830-
.classed("hiv-trace-legend multi_sequence", true)
4832+
.classed("hiv-trace-legend", true)
48314833
.attr("transform", "translate(0," + offset + ")")
48324834
.append("circle")
48334835
.attr("cx", "8")
48344836
.attr("cy", "-4")
48354837
.attr("r", "8")
4838+
.classed("multi_sequence", true)
48364839
.style("fill", "none");
48374840
self.legend_svg
48384841
.append("g")
@@ -6033,7 +6036,248 @@ var hivtrace_cluster_network_graph = function (
60336036
}
60346037

60356038
if (self._is_CDC_) {
6036-
self.draw_extended_node_table(self.aggregate_indvidual_level_records());
6039+
self.node_search_div = self.get_ui_element_selector_by_role(
6040+
"node_search_div",
6041+
true
6042+
);
6043+
6044+
if (self.node_search_div && self.isPrimaryGraph) {
6045+
const compute_type = (t, d) => {
6046+
if (t == "String") return "string";
6047+
if (t == "Number") return d.is_integer ? "integer" : "double";
6048+
if (t == "Date") return "date";
6049+
return "string";
6050+
};
6051+
6052+
self.node_search_attributes =
6053+
self._extract_exportable_attributes(false);
6054+
6055+
self.qb_filter_def = _.sortBy(
6056+
_.map(self.node_search_attributes, (d) => {
6057+
let def = {
6058+
id: d.raw_attribute_key,
6059+
label: d.label,
6060+
type: compute_type(d.type, d),
6061+
};
6062+
6063+
if (def.type == "date") {
6064+
def.plugin = "datepicker";
6065+
def.plugin_config = {
6066+
format: "yyyy/mm/dd",
6067+
todayBtn: "linked",
6068+
todayHighlight: true,
6069+
autoclose: true,
6070+
};
6071+
def.operators = [
6072+
"equal",
6073+
"not equal",
6074+
"less",
6075+
"less_or_equal",
6076+
"greater",
6077+
"greater_or_equal",
6078+
];
6079+
} else {
6080+
if (def.type == "string") {
6081+
def.operators = [
6082+
"equal",
6083+
"not equal",
6084+
"contains",
6085+
"begins_with",
6086+
"ends_with",
6087+
"is_empty",
6088+
"is_not_empty",
6089+
];
6090+
} else if (def.type == "integer" || def.type == "double") {
6091+
def.operators = [
6092+
"equal",
6093+
"not equal",
6094+
"less",
6095+
"less_or_equal",
6096+
"greater",
6097+
"greater_or_equal",
6098+
"between",
6099+
];
6100+
}
6101+
}
6102+
6103+
return def;
6104+
}),
6105+
(d) => d.label
6106+
);
6107+
6108+
let query_buttons = d3
6109+
.select(self.node_search_div)
6110+
.selectAll(
6111+
'[data-hivtrace-ui-role="node-selector-search-buttonbar"]'
6112+
);
6113+
6114+
if (query_buttons.empty()) {
6115+
d3.select(self.node_search_div)
6116+
.append("div")
6117+
.classed("alert alert-info alert-dismissible alert-small", true)
6118+
.text(
6119+
"Please define some search criteria to find and display information on persons in the network. By default, no persons are displayed."
6120+
)
6121+
.append("button")
6122+
.classed("close", true)
6123+
.attr("data-dismiss", "alert")
6124+
.append("span")
6125+
.html("&times;");
6126+
6127+
/*
6128+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
6129+
<span aria-hidden="true">&times;</span>
6130+
</button>
6131+
*/
6132+
6133+
query_buttons = d3
6134+
.select(self.node_search_div)
6135+
.append("div")
6136+
.classed("btn-group btn-group-sm", true)
6137+
.attr("data-hivtrace-ui-role", "node-selector-search-buttonbar");
6138+
self.node_query_button_reset = query_buttons
6139+
.append("button")
6140+
.text("Reset")
6141+
.classed("btn btn-warning", true);
6142+
self.node_query_button_search = query_buttons
6143+
.append("button")
6144+
.text("Search")
6145+
.classed("btn btn-primary", true);
6146+
}
6147+
6148+
$(self.node_search_div).queryBuilder({
6149+
plugins: [
6150+
"filter-description",
6151+
"unique-filter",
6152+
"bt-tooltip-errors",
6153+
"invert",
6154+
"not-group",
6155+
],
6156+
filters: self.qb_filter_def,
6157+
});
6158+
6159+
self.process_search_field = (value, condition) => {
6160+
switch (condition.type) {
6161+
case "string": {
6162+
if (!value) value = "";
6163+
value = value.toLowerCase();
6164+
switch (condition.operator) {
6165+
case "equal":
6166+
return value == condition.value;
6167+
case "not equal":
6168+
return value != condition.value;
6169+
case "contains":
6170+
return value.indexOf(condition.value) >= 0;
6171+
case "begins_with":
6172+
return value.indexOf(condition.value) == 0;
6173+
case "ends_with":
6174+
return (
6175+
value.indexOf(condition.value) ==
6176+
Math.max(0, value.length - condition.value.length)
6177+
);
6178+
case "is_not_empty":
6179+
return value.length > 0;
6180+
case "is_empty":
6181+
return value.length == 0;
6182+
}
6183+
break;
6184+
}
6185+
case "date":
6186+
case "integer":
6187+
case "double": {
6188+
if (!value) return false;
6189+
switch (condition.operator) {
6190+
case "equal":
6191+
return value == condition.value;
6192+
case "not equal":
6193+
return value != condition.value;
6194+
case "less":
6195+
return value < condition.value;
6196+
case "less_or_equal":
6197+
return value <= condition.value;
6198+
case "greater":
6199+
return value > condition.value;
6200+
case "greater_or_equal":
6201+
return value >= condition.value;
6202+
case "between":
6203+
return (
6204+
value >= condition.value[0] && value <= condition.value[1]
6205+
);
6206+
}
6207+
break;
6208+
}
6209+
}
6210+
return false;
6211+
};
6212+
6213+
self.process_search = (data, rules) => {
6214+
let rule_results;
6215+
if (rules.rules) {
6216+
rule_results = _.map(rules.rules, (r) => {
6217+
return self.process_search(data, r);
6218+
});
6219+
} else {
6220+
return self.process_search_field(
6221+
self.attribute_node_value_by_id(
6222+
data,
6223+
rules.id,
6224+
rules.type == "number"
6225+
),
6226+
rules
6227+
);
6228+
}
6229+
6230+
if (rules.condition == "AND") {
6231+
rule_results = _.every(rule_results, (d) => d);
6232+
} else if (rules.condition == "OR") {
6233+
rule_results = _.some(rule_results, (d) => d);
6234+
} else {
6235+
rule_results = false;
6236+
}
6237+
6238+
return rules.not ? !rule_results : rule_results;
6239+
};
6240+
6241+
self.rule_lc = (rules) => {
6242+
if (rules.rules) {
6243+
_.each(rules.rules, (r) => {
6244+
self.rule_lc(r);
6245+
});
6246+
} else {
6247+
if (rules.value) {
6248+
if (rules.type == "string") {
6249+
rules.value = rules.value.toLowerCase();
6250+
} else if (rules.type == "date") {
6251+
rules.value = timeDateUtil.DateViewNodeSearch.parse(
6252+
rules.value
6253+
);
6254+
}
6255+
}
6256+
}
6257+
};
6258+
6259+
if (!self.aggregate_entity_data) {
6260+
self.aggregate_entity_data =
6261+
self.aggregate_indvidual_level_records();
6262+
}
6263+
6264+
self.node_query_button_reset.on("click", (d) =>
6265+
$(self.node_search_div).queryBuilder("reset")
6266+
);
6267+
self.node_query_button_search.on("click", (d) => {
6268+
var result = $(self.node_search_div).queryBuilder("getRules");
6269+
if (!$.isEmptyObject(result)) {
6270+
self.rule_lc(result);
6271+
self.draw_extended_node_table(
6272+
_.filter(self.aggregate_entity_data, (d) =>
6273+
self.process_search(d, result)
6274+
)
6275+
);
6276+
}
6277+
});
6278+
}
6279+
6280+
self.draw_extended_node_table([]);
60376281
} else {
60386282
self.draw_node_table(self.extra_node_table_columns);
60396283
}
@@ -7906,7 +8150,7 @@ var hivtrace_cluster_network_graph = function (
79068150
var l_scale = 5000, // link scale
79078151
graph_data = self.json, // the raw JSON network object
79088152
max_points_to_render = 1536,
7909-
max_nodes_to_show = 16384,
8153+
max_nodes_to_show = 4096,
79108154
singletons = 0,
79118155
open_cluster_queue = [],
79128156
currently_displayed_objects,

0 commit comments

Comments
 (0)