Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

261 cosmograph data format #262

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions network/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ def nodes_icontains_filter(self, queryset, name, value):

def nodes_id_filter(self, queryset, name, value):
sane_values = [safe_int_conversion(x) for x in value]
print(value)
print(sane_values)
return queryset.filter(
Q(source_id__in=sane_values) | Q(target_id__in=sane_values)
)
Expand All @@ -56,7 +54,6 @@ def __init__(self, *args, **kwargs):
self.filters[field_name].help_text = model_field.help_text
except FieldDoesNotExist:
continue
print(field_name, type(model_field))
if isinstance(model_field, CharField) and not field_name == "edge_label":
if (
model_field.choices
Expand Down
18 changes: 13 additions & 5 deletions network/templates/network/list_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@
<h1 class="display-1 text-center">
<i class="bi bi-share"> Beziehungen</i>
</h1>
<div class="text-center">
<h2>Download</h2>
<div class="btn-group" role="group" class="text-end">
<a type="button" class="btn btn-outline-primary" href="{% url 'network:data' %}{% querystring %}&format=csv">CSV</a>
<a type="button" class="btn btn-outline-primary" href="{% url 'network:data' %}{% querystring %}&format=json">JSON</a>
<div class="row">
<div class="col-md-6 text-center">
<h2>Download</h2>
<div class="btn-group" role="group" class="text-end">
<a type="button" class="btn btn-outline-primary" href="{% url 'network:data' %}{% querystring %}&format=csv">CSV</a>
<a type="button" class="btn btn-outline-primary" href="{% url 'network:data' %}{% querystring %}&format=json">JSON</a>
</div>
</div>
<div class="col-md-6 text-center">
<h2>Visualize</h2>
<div class="btn-group" role="group" class="text-end">
<a type="button" class="btn btn-outline-primary" href="{% url 'dumper:network' %}{% querystring %}&format=cosmograph">Als Nezwerk</a>
</div>
</div>
</div>

Expand Down
48 changes: 45 additions & 3 deletions network/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,62 @@
content_type="text/csv",
)
format = request.GET.get("format", "csv")
if format not in ["csv", "json"]:
if format not in ["csv", "json", "cosmograph"]:
format = "csv"
if format == "csv":
response = HttpResponse(
content_type="text/csv",
headers={"Content-Disposition": 'attachment; filename="relations.csv"'},
)
df.to_csv(response, index=False)
else:
return response

Check warning on line 48 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L48

Added line #L48 was not covered by tests
elif format == "json":
df = df.set_index("edge_id")
df["start_date"] = pd.to_datetime(df["start_date"], errors="coerce")
df["end_date"] = pd.to_datetime(df["end_date"], errors="coerce")
df["start_date"] = df["start_date"].dt.strftime("%Y-%m-%d").fillna("")
df["end_date"] = df["end_date"].dt.strftime("%Y-%m-%d").fillna("")
out = df.to_json(orient="index", force_ascii=False)
response = JsonResponse(json.loads(out))
return response
elif format == "cosmograph":
data = {}
edge_data = df.apply(

Check warning on line 59 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L58-L59

Added lines #L58 - L59 were not covered by tests
lambda row: {
"id": row["edge_id"],
"s": row["source_id"],
"t": row["target_id"],
"start": str(row["start_date"]),
"end": str(row["end_date"]),
},
axis=1,
).tolist()
data["edges"] = edge_data
source_nodes = df[["source_label", "source_kind", "source_id"]].rename(

Check warning on line 70 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L69-L70

Added lines #L69 - L70 were not covered by tests
columns={
"source_label": "node_label",
"source_kind": "node_kind",
"source_id": "node_id",
}
)
target_nodes = df[["target_label", "target_kind", "target_id"]].rename(

Check warning on line 77 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L77

Added line #L77 was not covered by tests
columns={
"target_label": "node_label",
"target_kind": "node_kind",
"target_id": "node_id",
}
)
nodes = (

Check warning on line 84 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L84

Added line #L84 was not covered by tests
pd.concat([source_nodes, target_nodes])
.drop_duplicates()
.reset_index(drop=True)
)
data["nodes"] = nodes.apply(

Check warning on line 89 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L89

Added line #L89 was not covered by tests
lambda row: {
"id": row["node_id"],
"k": row["node_kind"],
"l": row["node_label"],
},
axis=1,
).tolist()
response = JsonResponse(data)
return response

Check warning on line 98 in network/views.py

View check run for this annotation

Codecov / codecov/patch

network/views.py#L97-L98

Added lines #L97 - L98 were not covered by tests
137 changes: 76 additions & 61 deletions static/src/js/main.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,76 @@
import {
Cosmograph,
CosmographSearch,
CosmographTimeline,
} from "@cosmograph/cosmograph";
import * as d3 from "d3";

const edge_csv = "/media/edges.csv";
const node_csv = "/media/nodes.csv";

const edge_promis = d3.csv(edge_csv);
const node_promis = d3.csv(node_csv);
const alertNode = document.getElementById("alert")
const spinnerNode = document.getElementById("spinner");
const legendNode = document.getElementById("legend");
const canvas = document.createElement("div");

Promise.all([edge_promis, node_promis]).then(([edge_data, node_data]) => {
const links = edge_data.map((d) => ({
source: parseInt(d.source),
target: parseInt(d.target),
date: Date.parse(d.date),
}));

const nodes = node_data.map((d) => ({
id: parseInt(d.id),
label: d.label,
color: d.color,
}));
const appNode = document.getElementById("app");

// remove spinner
spinnerNode.classList.add("visually-hidden");
legendNode.style.visibility = "visible";
alertNode.classList.add("visually-hidden");
appNode.appendChild(canvas);
const searchContainer = document.createElement("div");
appNode.appendChild(searchContainer);
const config = {
nodeColor: (d) => d.color,
nodeLabelAccessor: (d) => d.label,
showTopLabels: true,
showDynamicLabels: false,
linkGreyoutOpacity: 0,
nodeLabelColor: "white",
hoveredNodeLabelColor: "white",
linkWidth: 1,
linkArrows: false,
onClick: (data) => alert(data.label)
};
const cosmograph = new Cosmograph(canvas, config);

const timelineContainer = document.createElement("div");
const timeline = new CosmographTimeline(cosmograph, timelineContainer);


const search = new CosmographSearch(cosmograph, searchContainer);
cosmograph.setData(nodes, links);
appNode.appendChild(timelineContainer);

});
import { Cosmograph, CosmographTimeline } from "@cosmograph/cosmograph";

async function init() {
const alertNode = document.getElementById("alert");
const spinnerNode = document.getElementById("spinner");
const legendNode = document.getElementById("legend");
const canvas = document.createElement("div");

const queryString = window.location.search;
const url = `/network/csv/${queryString}`;

try {
const res = await fetch(url);
const data = await res.json();

const colors = {
person: "#720e07",
place: "#5bc0eb",
work: "#ff8600",
event: "#9bc53d",
institution: "#ffdd1b",
};

const links = data["edges"].map((d) => ({
source: parseInt(d.s),
target: parseInt(d.t),
date: Date.parse(d.start),
}));

const nodes = data["nodes"].map((d) => ({
id: parseInt(d.id),
label: d.l,
color: colors[d["k"]],
}));

const appNode = document.getElementById("app");

// Remove spinner
spinnerNode.classList.add("visually-hidden");
legendNode.style.visibility = "visible";
alertNode.classList.add("visually-hidden");

appNode.appendChild(canvas);
const searchContainer = document.createElement("div");
appNode.appendChild(searchContainer);

const config = {
nodeColor: (d) => d.color,
nodeLabelAccessor: (d) => d.label,
showTopLabels: true,
showDynamicLabels: false,
linkGreyoutOpacity: 0,
nodeLabelColor: "white",
hoveredNodeLabelColor: "white",
linkWidth: 1,
linkArrows: false,
onClick: (data) => alert(data.label),
simulationRepulsion: 1,
linkDistance: 5,
gravity: 0.5
};

const cosmograph = new Cosmograph(canvas, config);

const timelineContainer = document.createElement("div");
const timeline = new CosmographTimeline(cosmograph, timelineContainer);
cosmograph.setData(nodes, links);
appNode.appendChild(timelineContainer);
} catch (error) {
console.error("Failed to fetch data:", error);
alertNode.textContent = "Failed to load data. Please try again later.";
alertNode.style.visibility = "visible";
}
}

init();

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

951 changes: 0 additions & 951 deletions static/vite/main-CJBCyQtR.js

This file was deleted.

899 changes: 899 additions & 0 deletions static/vite/main-Dnusw_Zv.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions static/vite/manifest.info
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"_browser-CBdNgIts.js": {
"file": "browser-CBdNgIts.js",
"_browser-lg2ZDdSy.js": {
"file": "browser-lg2ZDdSy.js",
"name": "browser",
"isDynamicEntry": true,
"imports": [
"js/main.js"
]
},
"js/main.js": {
"file": "main-CJBCyQtR.js",
"file": "main-Dnusw_Zv.js",
"name": "main",
"src": "js/main.js",
"isEntry": true,
"dynamicImports": [
"_browser-CBdNgIts.js"
"_browser-lg2ZDdSy.js"
]
}
}