mirror of
https://github.com/MISP/misp-galaxy.git
synced 2024-11-26 16:57:18 +00:00
Add [graph] filtering based on table
This commit is contained in:
parent
d357075432
commit
590a05e3c7
4 changed files with 148 additions and 28 deletions
|
@ -93,7 +93,6 @@ class Galaxy():
|
||||||
self.entry += f' |{author}|\n'
|
self.entry += f' |{author}|\n'
|
||||||
|
|
||||||
def _create_clusters(self):
|
def _create_clusters(self):
|
||||||
# global public_clusters_dict
|
|
||||||
clusters = []
|
clusters = []
|
||||||
for cluster in self.cluster_list:
|
for cluster in self.cluster_list:
|
||||||
clusters.append(Cluster(
|
clusters.append(Cluster(
|
||||||
|
@ -195,18 +194,11 @@ class Cluster():
|
||||||
self.entry += f'\n'
|
self.entry += f'\n'
|
||||||
self.entry += f'??? info "Associated metadata"\n'
|
self.entry += f'??? info "Associated metadata"\n'
|
||||||
self.entry += f'\n'
|
self.entry += f'\n'
|
||||||
self.entry += f' <div class="no-filter">\n'
|
|
||||||
self.entry += f' \n'
|
|
||||||
self.entry += f' |Metadata key {{ .no-filter }} |Value|\n'
|
self.entry += f' |Metadata key {{ .no-filter }} |Value|\n'
|
||||||
self.entry += f' |-----------------------------------|-----|\n'
|
self.entry += f' |-----------------------------------|-----|\n'
|
||||||
for meta in sorted(self.meta.keys()):
|
for meta in sorted(self.meta.keys()):
|
||||||
if meta not in excluded_meta:
|
if meta not in excluded_meta:
|
||||||
self.entry += f' | {meta} | {self.meta[meta]} |\n'
|
self.entry += f' | {meta} | {self.meta[meta]} |\n'
|
||||||
# self.entry += " {: .no-filter }\n"
|
|
||||||
self.entry += f' \n'
|
|
||||||
self.entry += f' </div>\n'
|
|
||||||
self.entry += f'\n'
|
|
||||||
|
|
||||||
|
|
||||||
def get_related_clusters(self, cluster_dict, depth=-1, visited=None, level=1):
|
def get_related_clusters(self, cluster_dict, depth=-1, visited=None, level=1):
|
||||||
global public_relations_count
|
global public_relations_count
|
||||||
|
|
158
tools/mkdocs/site/docs/javascripts/d3.js
vendored
158
tools/mkdocs/site/docs/javascripts/d3.js
vendored
|
@ -1,28 +1,43 @@
|
||||||
document$.subscribe(function () {
|
document$.subscribe(function () {
|
||||||
// Function to parse table and return data
|
// Function to parse table and return data
|
||||||
function parseTable(table) {
|
// function parseTable(table) {
|
||||||
|
// var data = [];
|
||||||
|
// var rows = table.querySelectorAll("tr");
|
||||||
|
// rows.forEach((row, i) => {
|
||||||
|
// // Skipping header row and filter row
|
||||||
|
// if (i > 1) {
|
||||||
|
// var cells = row.querySelectorAll("td");
|
||||||
|
// data.push({ source: cells[0].textContent, target: cells[1].textContent, level: cells[2].textContent });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// return data;
|
||||||
|
// }
|
||||||
|
|
||||||
|
function parseFilteredTable(tf) {
|
||||||
var data = [];
|
var data = [];
|
||||||
var rows = table.querySelectorAll("tr");
|
tf.getFilteredData().forEach((row, i) => {
|
||||||
rows.forEach((row, i) => {
|
// console.log("Row");
|
||||||
// Skipping header row and filter row
|
// console.log(row);
|
||||||
if (i > 1) {
|
data.push({ source: row[1][0], target: row[1][1], level: row[1][2] });
|
||||||
var cells = row.querySelectorAll("td");
|
// console.log("Data");
|
||||||
data.push({ source: cells[0].textContent, target: cells[1].textContent, level: cells[2].textContent });
|
// console.log(data);
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to create graph
|
// Function to create Force-Directed Graph
|
||||||
function createGraph(data, elementId) {
|
function createForceDirectedGraph(data, elementId) {
|
||||||
// Extract nodes and links
|
// Extract nodes and links
|
||||||
var nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])))
|
var nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])))
|
||||||
.map(id => ({ id }));
|
.map(id => ({ id }));
|
||||||
|
|
||||||
var links = data.map(d => ({ source: d.source, target: d.target }));
|
var links = data.map(d => ({ source: d.source, target: d.target }));
|
||||||
|
// console.log("Nodes");
|
||||||
|
// console.log(nodes);
|
||||||
|
|
||||||
// Set up the dimensions of the graph
|
// Set up the dimensions of the graph
|
||||||
var width = 800, height = 600;
|
var width = 1000, height = 1000;
|
||||||
|
|
||||||
// Append SVG for the graph
|
// Append SVG for the graph
|
||||||
var svg = d3.select(elementId).append("svg")
|
var svg = d3.select(elementId).append("svg")
|
||||||
|
@ -66,19 +81,132 @@ document$.subscribe(function () {
|
||||||
.attr("cx", d => d.x)
|
.attr("cx", d => d.x)
|
||||||
.attr("cy", d => d.y);
|
.attr("cy", d => d.y);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// return simulation;
|
||||||
|
// invalidation.then(() => simulation.stop());
|
||||||
|
return Object.assign(svg.node(), {
|
||||||
|
update({ newNodes, newLinks }) {
|
||||||
|
// Process new nodes and maintain the existing ones
|
||||||
|
const oldNodesMap = new Map(node.data().map(d => [d.id, d]));
|
||||||
|
nodes = newNodes.map(d => Object.assign(oldNodesMap.get(d.id) || {}, d));
|
||||||
|
|
||||||
|
// Update nodes with new data
|
||||||
|
node = node.data(nodes, d => d.id)
|
||||||
|
.join(
|
||||||
|
enter => enter.append("circle")
|
||||||
|
.attr("r", 5)
|
||||||
|
.attr("fill", "#69b3a2"),
|
||||||
|
update => update,
|
||||||
|
exit => exit.remove()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Process new links
|
||||||
|
const oldLinksMap = new Map(link.data().map(d => [`${d.source.id},${d.target.id}`, d]));
|
||||||
|
links = newLinks.map(d => Object.assign(oldLinksMap.get(`${d.source.id},${d.target.id}`) || {}, d));
|
||||||
|
|
||||||
|
// Update links with new data
|
||||||
|
link = link.data(links, d => `${d.source.id},${d.target.id}`)
|
||||||
|
.join(
|
||||||
|
enter => enter.append("line")
|
||||||
|
.attr("stroke-width", d => Math.sqrt(d.value)),
|
||||||
|
update => update,
|
||||||
|
exit => exit.remove()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restart the simulation with new data
|
||||||
|
simulation.nodes(nodes);
|
||||||
|
simulation.force("link").links(links);
|
||||||
|
simulation.alpha(1).restart();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all tables that have a th with the class .graph and generate graphs
|
// Find all tables that have a th with the class .graph and generate Force-Directed Graphs
|
||||||
document.querySelectorAll("table").forEach((table, index) => {
|
document.querySelectorAll("table").forEach((table, index) => {
|
||||||
var graphHeader = table.querySelector("th.graph");
|
var graphHeader = table.querySelector("th.graph");
|
||||||
if (graphHeader) {
|
if (graphHeader) {
|
||||||
var data = parseTable(table);
|
// var data = parseTable(table);
|
||||||
console.log(data);
|
// //var data = parseFilteredTable(table);
|
||||||
|
// // console.log("Data");
|
||||||
|
// // console.log(data);
|
||||||
|
// var graphId = "graph" + index;
|
||||||
|
// var div = document.createElement("div");
|
||||||
|
// div.id = graphId;
|
||||||
|
// table.after(div);
|
||||||
|
// var simulation = createForceDirectedGraph(data, "#" + graphId);
|
||||||
|
|
||||||
|
// Initialize TableFilter for the table
|
||||||
|
var tf = new TableFilter(table, {
|
||||||
|
// Define filter options for each column (customize as needed)
|
||||||
|
base_path: "https://unpkg.com/tablefilter@0.7.3/dist/tablefilter/",
|
||||||
|
highlight_keywords: true,
|
||||||
|
col_2: "checklist",
|
||||||
|
col_widths: ["350px", "350px", "100px"],
|
||||||
|
col_types: ["string", "string", "number"],
|
||||||
|
grid_layout: false,
|
||||||
|
responsive: false,
|
||||||
|
watermark: ["Filter table ...", "Filter table ..."],
|
||||||
|
auto_filter: {
|
||||||
|
delay: 100 //milliseconds
|
||||||
|
},
|
||||||
|
filters_row_index: 1,
|
||||||
|
state: true,
|
||||||
|
rows_counter: true,
|
||||||
|
status_bar: true,
|
||||||
|
themes: [{
|
||||||
|
name: "transparent",
|
||||||
|
}],
|
||||||
|
btn_reset: {
|
||||||
|
tooltip: "Reset",
|
||||||
|
toolbar_position: "right",
|
||||||
|
},
|
||||||
|
toolbar: true,
|
||||||
|
extensions: [{
|
||||||
|
name: "sort",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'filtersVisibility',
|
||||||
|
description: 'Sichtbarkeit der Filter',
|
||||||
|
toolbar_position: 'right',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
tf.init();
|
||||||
|
//var data = parseTable(table);
|
||||||
|
var data = parseFilteredTable(tf);
|
||||||
|
// console.log("Data");
|
||||||
|
// console.log(data);
|
||||||
var graphId = "graph" + index;
|
var graphId = "graph" + index;
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
div.id = graphId;
|
div.id = graphId;
|
||||||
table.after(div);
|
table.after(div);
|
||||||
createGraph(data, "#" + graphId);
|
var simulation = createForceDirectedGraph(data, "#" + graphId);
|
||||||
|
|
||||||
|
// Function to filter the table data and update the graph
|
||||||
|
function filterTableAndGraph() {
|
||||||
|
var filteredData = parseFilteredTable(tf);
|
||||||
|
// console.log("Filtered Data");
|
||||||
|
// console.log(filteredData);
|
||||||
|
var { newNodes, newLinks } = processNewData(filteredData);
|
||||||
|
|
||||||
|
// Restart the simulation with filtered data
|
||||||
|
simulation.update({ newNodes: newNodes, newLinks: newLinks });
|
||||||
|
}
|
||||||
|
|
||||||
|
function processNewData(newData) {
|
||||||
|
// Extracting new nodes
|
||||||
|
var newNodes = Array.from(new Set(newData.flatMap(d => [d.source, d.target])))
|
||||||
|
.map(id => ({ id }));
|
||||||
|
|
||||||
|
// Preparing the links in the required format
|
||||||
|
var newLinks = newData.map(d => ({ source: d.source, target: d.target }));
|
||||||
|
// console.log("New Nodes");
|
||||||
|
// console.log(newNodes);
|
||||||
|
return { newNodes, newLinks };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for table filtering events
|
||||||
|
tf.emitter.on(['after-filtering'], filterTableAndGraph);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,9 +32,9 @@ document$.subscribe(function () {
|
||||||
tooltip: "Reset",
|
tooltip: "Reset",
|
||||||
toolbar_position: "right",
|
toolbar_position: "right",
|
||||||
},
|
},
|
||||||
no_results_message: {
|
// no_results_message: {
|
||||||
content: "No matching records found",
|
// content: "No matching records found",
|
||||||
},
|
// },
|
||||||
toolbar: true,
|
toolbar: true,
|
||||||
extensions: [{
|
extensions: [{
|
||||||
name: "sort",
|
name: "sort",
|
||||||
|
|
|
@ -66,7 +66,7 @@ extra:
|
||||||
generator: false
|
generator: false
|
||||||
|
|
||||||
extra_javascript:
|
extra_javascript:
|
||||||
- javascripts/tablefilter.js
|
# - javascripts/tablefilter.js
|
||||||
- "https://unpkg.com/tablefilter@0.7.3/dist/tablefilter/tablefilter.js"
|
- "https://unpkg.com/tablefilter@0.7.3/dist/tablefilter/tablefilter.js"
|
||||||
- "https://d3js.org/d3.v6.min.js"
|
- "https://d3js.org/d3.v6.min.js"
|
||||||
- javascripts/d3.js
|
- javascripts/d3.js
|
||||||
|
|
Loading…
Reference in a new issue