From d3570754328357e05e48872c277246a541ae7906 Mon Sep 17 00:00:00 2001 From: niclas Date: Fri, 2 Feb 2024 14:10:57 +0100 Subject: [PATCH] Add [graph] basic graph --- tools/mkdocs/.gitignore | 3 +- tools/mkdocs/generator.py | 55 ++++++++---- tools/mkdocs/site/docs/javascripts/d3.js | 84 +++++++++++++++++ .../site/docs/javascripts/tablefilter.js | 89 ++++++++++--------- tools/mkdocs/site/mkdocs.yml | 2 + 5 files changed, 171 insertions(+), 62 deletions(-) create mode 100644 tools/mkdocs/site/docs/javascripts/d3.js diff --git a/tools/mkdocs/.gitignore b/tools/mkdocs/.gitignore index 4a90ee28..13ec291a 100644 --- a/tools/mkdocs/.gitignore +++ b/tools/mkdocs/.gitignore @@ -1,4 +1,5 @@ /site/docs/* !/site/docs/javascripts -/site/site \ No newline at end of file +/site/site +/javascripts \ No newline at end of file diff --git a/tools/mkdocs/generator.py b/tools/mkdocs/generator.py index 0ba80d44..e993aeee 100644 --- a/tools/mkdocs/generator.py +++ b/tools/mkdocs/generator.py @@ -103,7 +103,8 @@ class Galaxy(): date=cluster.get('date', None), related_list=cluster.get('related', None), meta=cluster.get('meta', None), - galaxie=self.json_file_name + galaxie=self.name, + galaxie_file_name=self.json_file_name )) return clusters @@ -128,7 +129,7 @@ class Galaxy(): index.write(self.entry) class Cluster(): - def __init__(self, description, uuid, date, value, related_list, meta, galaxie): + def __init__(self, description, uuid, date, value, related_list, meta, galaxie, galaxie_file_name): self.description = description self.uuid = uuid self.date = date @@ -137,6 +138,7 @@ class Cluster(): self.meta = meta self.entry = "" self.galaxie = galaxie + self.galaxie_file_name = galaxie_file_name global public_clusters_dict if self.galaxie: @@ -193,11 +195,17 @@ class Cluster(): self.entry += f'\n' self.entry += f'??? info "Associated metadata"\n' self.entry += f'\n' - self.entry += f' |Metadata key |Value|\n' - self.entry += f' |------------------|-----|\n' + self.entry += f'
\n' + self.entry += f' \n' + self.entry += f' |Metadata key {{ .no-filter }} |Value|\n' + self.entry += f' |-----------------------------------|-----|\n' for meta in sorted(self.meta.keys()): if meta not in excluded_meta: self.entry += f' | {meta} | {self.meta[meta]} |\n' + # self.entry += " {: .no-filter }\n" + self.entry += f' \n' + self.entry += f'
\n' + self.entry += f'\n' def get_related_clusters(self, cluster_dict, depth=-1, visited=None, level=1): @@ -228,7 +236,7 @@ class Cluster(): private_relations_count += 1 if dest_uuid not in private_clusters: private_clusters.append(dest_uuid) - related_clusters.append((self, Cluster(value="Private Cluster", uuid=dest_uuid, date=None, description=None, related_list=None, meta=None, galaxie=None), level)) + related_clusters.append((self, Cluster(value="Private Cluster", uuid=dest_uuid, date=None, description=None, related_list=None, meta=None, galaxie=None, galaxie_file_name=None), level)) continue related_cluster = cluster_dict[dest_uuid] @@ -261,10 +269,12 @@ class Cluster(): output = "" output += f'## Related clusters for {self.value}\n' output += f'\n' - output += f'| Cluster A | Cluster B | Level |\n' + output += f'| Cluster A | Cluster B | Level {{ .graph }} |\n' output += f'|-----------|-----------|-------|\n' for relation in relations: - output += f'| {relation[0].value} ({relation[0].uuid}) | {relation[1].value} ({relation[1].uuid}) | {relation[2]} |\n' + cluster_a_section = relation[0].value.lower().replace(" ", "+").replace("/", "").replace(":", "") + cluster_b_section = relation[1].value.lower().replace(" ", "+").replace("/", "").replace(":", "") + output += f'| [{relation[0].value} ({relation[0].uuid})](../../{relation[0].galaxie_file_name}/index.md#{cluster_a_section}) | [{relation[1].value} ({relation[1].uuid})](../../{relation[1].galaxie_file_name}/index.md#{cluster_b_section}) | {relation[2]} |\n' return output def create_entry(self, cluster_dict): @@ -283,7 +293,7 @@ class Cluster(): related_clusters = self.get_related_clusters(cluster_dict) global relation_count_dict relation_count_dict[self.value] = len(related_clusters) - galaxy_path = os.path.join(path, self.galaxie) + galaxy_path = os.path.join(path, self.galaxie_file_name) if not os.path.exists(galaxy_path): os.mkdir(galaxy_path) relation_path = os.path.join(galaxy_path, 'relations') @@ -356,16 +366,23 @@ def create_statistics(): for galaxy in public_clusters_dict.values(): galaxy_counts[galaxy] = galaxy_counts.get(galaxy, 0) + 1 top_galaxies, top_galaxies_values = get_top_x(galaxy_counts, 25) - statistic_output += create_xy_chart("Galaxies with the most clusters", 2500, 500, top_galaxies, "Number of clusters", top_galaxies_values) + statistic_output += create_xy_chart("Galaxies with the most clusters", 3000, 1000, top_galaxies, "Number of clusters", top_galaxies_values) + for i, galaxy in enumerate(top_galaxies.split(", "), 1): + statistic_output += f'{i}. [{galaxy}](./{galaxy}/index.md)\n' + statistic_output += f'\n' statistic_output += f'## Galaxies with the least clusters\n' flop_galaxies, flop_galaxies_values = get_top_x(galaxy_counts, 25, False) - statistic_output += create_xy_chart("Galaxies with the least clusters", 2500, 500, flop_galaxies, "Number of clusters", flop_galaxies_values) + statistic_output += create_xy_chart("Galaxies with the least clusters", 3000, 1000, flop_galaxies, "Number of clusters", flop_galaxies_values) + for i, galaxy in enumerate(flop_galaxies.split(", "), 1): + statistic_output += f'{i}. [{galaxy}](./{galaxy}/index.md)\n' + statistic_output += f'\n' - galaxy_number = 0 - for galaxy in public_clusters_dict.values(): - galaxy_number += 1 - statistic_output += f'**Average number of clusters per galaxy**: {len(public_clusters_dict) / galaxy_number}\n' + # galaxy_number = 0 + # global galaxies + # for galaxy in galaxies: + # galaxy_number += 1 + # statistic_output += f'**Average number of clusters per galaxy**: {len(public_clusters_dict) / galaxy_number}\n' statistic_output += f'# Relation statistics\n' statistic_output += f'## Number of relations\n' @@ -375,20 +392,20 @@ def create_statistics(): statistic_output += f'## Cluster with the most relations\n' top_25_relation, top_25_relation_values = get_top_x(relation_count_dict, 25) - statistic_output += create_xy_chart("Cluster with the most relations", 2500, 500, top_25_relation, "Number of relations", top_25_relation_values) + statistic_output += create_xy_chart("Cluster with the most relations", 3000, 1000, top_25_relation, "Number of relations", top_25_relation_values) statistic_output += f'## Cluster with the least relations\n' top_25_relation, top_25_relation_values = get_top_x(relation_count_dict, 25, False) - statistic_output += create_xy_chart("Cluster with the least relations", 2500, 500, top_25_relation, "Number of relations", top_25_relation_values) + statistic_output += create_xy_chart("Cluster with the least relations", 3000, 1000, top_25_relation, "Number of relations", top_25_relation_values) statistic_output += f'# Synonyms statistics\n' statistic_output += f'## Cluster with the most synonyms\n' top_25_synonyms, top_25_synonyms_values = get_top_x(synonyms_count_dict, 25) - statistic_output += create_xy_chart("Cluster with the most synonyms", 2500, 500, top_25_synonyms, "Number of synonyms", top_25_synonyms_values) + statistic_output += create_xy_chart("Cluster with the most synonyms", 3000, 1000, top_25_synonyms, "Number of synonyms", top_25_synonyms_values) statistic_output += f'## Cluster with the least synonyms\n' top_25_synonyms, top_25_synonyms_values = get_top_x(synonyms_count_dict, 25, False) - statistic_output += create_xy_chart("Cluster with the least synonyms", 2500, 500, top_25_synonyms, "Number of synonyms", top_25_synonyms_values) + statistic_output += create_xy_chart("Cluster with the least synonyms", 3000, 1000, top_25_synonyms, "Number of synonyms", top_25_synonyms_values) statistic_output += f'# Empty UUIDs statistics\n' statistic_output += f'**Number of empty UUIDs**: {sum(empty_uuids_dict.values())}\n' @@ -441,7 +458,7 @@ def main(): # for galaxy in galaxies: # galaxy.write_entry(SITE_PATH, cluster_dict) - count = 3 + count = 10 for galaxy in galaxies: galaxy.write_entry(SITE_PATH, cluster_dict) count -= 1 diff --git a/tools/mkdocs/site/docs/javascripts/d3.js b/tools/mkdocs/site/docs/javascripts/d3.js new file mode 100644 index 00000000..9ced2fc5 --- /dev/null +++ b/tools/mkdocs/site/docs/javascripts/d3.js @@ -0,0 +1,84 @@ +document$.subscribe(function () { + // Function to parse table and return data + 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 to create graph + function createGraph(data, elementId) { + // Extract nodes and links + var nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target]))) + .map(id => ({ id })); + + var links = data.map(d => ({ source: d.source, target: d.target })); + + // Set up the dimensions of the graph + var width = 800, height = 600; + + // Append SVG for the graph + var svg = d3.select(elementId).append("svg") + .attr("width", width) + .attr("height", height); + + // Create a force simulation + var simulation = d3.forceSimulation(nodes) + .force("link", d3.forceLink(links).id(d => d.id)) + .force("charge", d3.forceManyBody()) + .force("center", d3.forceCenter(width / 2, height / 2)); + + // Create links + var link = svg.append("g") + .attr("stroke", "#999") + .attr("stroke-opacity", 0.6) + .selectAll("line") + .data(links) + .enter().append("line") + .attr("stroke-width", d => Math.sqrt(d.value)); + + // Create nodes + var node = svg.append("g") + .attr("stroke", "#fff") + .attr("stroke-width", 1.5) + .selectAll("circle") + .data(nodes) + .enter().append("circle") + .attr("r", 5) + .attr("fill", "#69b3a2"); + + // Update positions on each simulation 'tick' + simulation.on("tick", () => { + link + .attr("x1", d => d.source.x) + .attr("y1", d => d.source.y) + .attr("x2", d => d.target.x) + .attr("y2", d => d.target.y); + + node + .attr("cx", d => d.x) + .attr("cy", d => d.y); + }); + } + + // Find all tables that have a th with the class .graph and generate graphs + document.querySelectorAll("table").forEach((table, index) => { + var graphHeader = table.querySelector("th.graph"); + if (graphHeader) { + var data = parseTable(table); + console.log(data); + var graphId = "graph" + index; + var div = document.createElement("div"); + div.id = graphId; + table.after(div); + createGraph(data, "#" + graphId); + } + }); +}); diff --git a/tools/mkdocs/site/docs/javascripts/tablefilter.js b/tools/mkdocs/site/docs/javascripts/tablefilter.js index 7463906f..b9314ada 100644 --- a/tools/mkdocs/site/docs/javascripts/tablefilter.js +++ b/tools/mkdocs/site/docs/javascripts/tablefilter.js @@ -1,48 +1,53 @@ document$.subscribe(function () { - var tables = document.querySelectorAll("article table:not([class])") + var tables = document.querySelectorAll("article table") tables.forEach(function (table) { - var tf = new TableFilter(table, { - base_path: "https://unpkg.com/tablefilter@0.7.3/dist/tablefilter/", - highlight_keywords: true, - // col_0: "select", - // col_1: "select", - col_2: "checklist", - col_widths: ["350px", "350px", "100px"], - col_types: ["string", "string", "number"], - grid_layout: false, - responsive: false, - watermark: ["Filter table ...", "Filter table ..."], + var excludeTable = table.querySelector("td.no-filter, th.no-filter"); + if (!excludeTable) { + var tf = new TableFilter(table, { + base_path: "https://unpkg.com/tablefilter@0.7.3/dist/tablefilter/", + highlight_keywords: true, + // col_0: "select", + // col_1: "select", + 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, - // alternate_rows: true, - rows_counter: true, - status_bar: true, + auto_filter: { + delay: 100 //milliseconds + }, + filters_row_index: 1, + state: true, + // alternate_rows: true, + rows_counter: true, + status_bar: true, - themes: [{ - name: "transparent", - }], + themes: [{ + name: "transparent", + }], - btn_reset: { - tooltip: "Reset", - toolbar_position: "right", - }, - no_results_message: { - content: "No matching records found", - }, - toolbar: true, - extensions: [{ - name: "sort", - }, - { - name: 'filtersVisibility', - description: 'Sichtbarkeit der Filter', - toolbar_position: 'right', - },], - }) - tf.init() + btn_reset: { + tooltip: "Reset", + toolbar_position: "right", + }, + no_results_message: { + content: "No matching records found", + }, + toolbar: true, + extensions: [{ + name: "sort", + }, + { + name: 'filtersVisibility', + description: 'Sichtbarkeit der Filter', + toolbar_position: 'right', + },], + }) + tf.init() + } }) -}) \ No newline at end of file +}) + + diff --git a/tools/mkdocs/site/mkdocs.yml b/tools/mkdocs/site/mkdocs.yml index 71ecf1d2..43602cc2 100644 --- a/tools/mkdocs/site/mkdocs.yml +++ b/tools/mkdocs/site/mkdocs.yml @@ -68,6 +68,8 @@ extra: extra_javascript: - javascripts/tablefilter.js - "https://unpkg.com/tablefilter@0.7.3/dist/tablefilter/tablefilter.js" + - "https://d3js.org/d3.v6.min.js" + - javascripts/d3.js plugins: - search