darabos commited on
Commit
a18645a
·
1 Parent(s): 9e91869

Simple input conversion. Simple table viewer.

Browse files
server/basic_ops.py CHANGED
@@ -14,11 +14,11 @@ def create_scale_free_graph(*, nodes: int = 10):
14
  return nx.scale_free_graph(nodes)
15
 
16
  @ops.op("Compute PageRank")
17
- def compute_pagerank(graph, *, damping: 0.85, iterations: 3):
18
  return nx.pagerank(graph)
19
 
20
  @ops.op("Visualize graph")
21
- def visualize_graph(graph) -> 'graphviz':
22
  return {
23
  'attributes': {
24
  'name': 'My Graph'
@@ -40,3 +40,15 @@ def visualize_graph(graph) -> 'graphviz':
40
  }
41
  ]
42
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  return nx.scale_free_graph(nodes)
15
 
16
  @ops.op("Compute PageRank")
17
+ def compute_pagerank(graph: nx.Graph, *, damping: 0.85, iterations: 3):
18
  return nx.pagerank(graph)
19
 
20
  @ops.op("Visualize graph")
21
+ def visualize_graph(graph: ops.Bundle) -> 'graph_view':
22
  return {
23
  'attributes': {
24
  'name': 'My Graph'
 
40
  }
41
  ]
42
  }
43
+
44
+ @ops.op("View table")
45
+ def view_table(dfs: ops.Bundle) -> 'table_view':
46
+ v = {
47
+ 'dataframes': { name: {
48
+ 'columns': [str(c) for c in df.columns],
49
+ 'data': df.values.tolist(),
50
+ } for name, df in dfs.dfs.items() },
51
+ 'edges': dfs.edges,
52
+ }
53
+ print(v)
54
+ return v
server/main.py CHANGED
@@ -72,8 +72,8 @@ def execute(ws):
72
  continue
73
  data.error = None
74
  outputs[node.id] = output
75
- if op.type == 'graphviz':
76
- data.graph = output
77
 
78
 
79
  @app.post("/api/save")
 
72
  continue
73
  data.error = None
74
  outputs[node.id] = output
75
+ if op.type == 'graph_view' or op.type == 'table_view':
76
+ data.view = output
77
 
78
 
79
  @app.post("/api/save")
server/ops.py CHANGED
@@ -1,6 +1,8 @@
1
  '''API for implementing LynxKite operations.'''
2
  import dataclasses
3
  import inspect
 
 
4
 
5
  ALL_OPS = {}
6
 
@@ -21,6 +23,14 @@ class Op:
21
  t = sig.parameters[p].annotation
22
  if t == int:
23
  params[p] = int(params[p])
 
 
 
 
 
 
 
 
24
  res = self.func(*inputs, **params)
25
  return res
26
 
@@ -39,6 +49,33 @@ class Bundle:
39
  dfs: dict
40
  edges: list[EdgeDefinition]
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def op(name):
43
  '''Decorator for defining an operation.'''
44
  def decorator(func):
@@ -53,7 +90,8 @@ def op(name):
53
  name: param.default if param.default is not inspect._empty else None
54
  for name, param in sig.parameters.items()
55
  if param.kind == param.KEYWORD_ONLY}
56
- op = Op(func, name, params=params, inputs=inputs, outputs={'output': 'yes'}, type=type)
 
57
  ALL_OPS[name] = op
58
  return func
59
  return decorator
 
1
  '''API for implementing LynxKite operations.'''
2
  import dataclasses
3
  import inspect
4
+ import networkx as nx
5
+ import pandas as pd
6
 
7
  ALL_OPS = {}
8
 
 
23
  t = sig.parameters[p].annotation
24
  if t == int:
25
  params[p] = int(params[p])
26
+ # Convert inputs.
27
+ inputs = list(inputs)
28
+ for i, (x, p) in enumerate(zip(inputs, sig.parameters.values())):
29
+ t = p.annotation
30
+ if t == nx.Graph and isinstance(x, Bundle):
31
+ inputs[i] = o.to_nx()
32
+ elif t == Bundle and isinstance(x, nx.Graph):
33
+ inputs[i] = Bundle.from_nx(x)
34
  res = self.func(*inputs, **params)
35
  return res
36
 
 
49
  dfs: dict
50
  edges: list[EdgeDefinition]
51
 
52
+ @classmethod
53
+ def from_nx(cls, graph: nx.Graph):
54
+ edges = nx.to_pandas_edgelist(graph)
55
+ print(edges)
56
+ nodes = pd.DataFrame({'id': list(graph.nodes)})
57
+ print(nodes)
58
+ return cls(
59
+ dfs={'edges': edges, 'nodes': nodes},
60
+ edges=[
61
+ EdgeDefinition(
62
+ df='edges',
63
+ source_column='source',
64
+ target_column='target',
65
+ source_table='nodes',
66
+ target_table='nodes',
67
+ source_key='id',
68
+ target_key='id',
69
+ )
70
+ ]
71
+ )
72
+
73
+ def to_nx(self):
74
+ graph = nx.from_pandas_edgelist(self.dfs['edges'])
75
+ nx.set_node_attributes(graph, self.dfs['nodes'].set_index('id').to_dict('index'))
76
+ return graph
77
+
78
+
79
  def op(name):
80
  '''Decorator for defining an operation.'''
81
  def decorator(func):
 
90
  name: param.default if param.default is not inspect._empty else None
91
  for name, param in sig.parameters.items()
92
  if param.kind == param.KEYWORD_ONLY}
93
+ outputs = {'output': 'yes'} if type == 'basic' else {} # Maybe more fancy later.
94
+ op = Op(func, name, params=params, inputs=inputs, outputs=outputs, type=type)
95
  ALL_OPS[name] = op
96
  return func
97
  return decorator
web/src/LynxKiteFlow.svelte CHANGED
@@ -13,7 +13,8 @@
13
  type Edge,
14
  } from '@xyflow/svelte';
15
  import NodeWithParams from './NodeWithParams.svelte';
16
- import NodeWithGraphVisualization from './NodeWithGraphVisualization.svelte';
 
17
  import NodeSearch from './NodeSearch.svelte';
18
  import '@xyflow/svelte/dist/style.css';
19
 
@@ -21,33 +22,11 @@
21
 
22
  const nodeTypes: NodeTypes = {
23
  basic: NodeWithParams,
24
- graphviz: NodeWithGraphVisualization,
 
25
  };
26
 
27
- const nodes = writable<Node[]>([
28
- {
29
- id: '1',
30
- type: 'basic',
31
- data: { title: 'Compute PageRank', params: { damping: 0.85, iterations: 3 } },
32
- position: { x: 0, y: 0 },
33
- sourcePosition: Position.Right,
34
- targetPosition: Position.Left,
35
- },
36
- {
37
- id: '3',
38
- type: 'basic',
39
- data: { title: 'Import Parquet', params: { filename: '/tmp/x.parquet' } },
40
- position: { x: -400, y: 0 },
41
- sourcePosition: Position.Right,
42
- },
43
- {
44
- id: '4',
45
- type: 'graphviz',
46
- data: { title: 'Visualize graph', params: {} },
47
- position: { x: 300, y: 0 },
48
- targetPosition: Position.Left,
49
- },
50
- ]);
51
 
52
  const edges = writable<Edge[]>([
53
  {
 
13
  type Edge,
14
  } from '@xyflow/svelte';
15
  import NodeWithParams from './NodeWithParams.svelte';
16
+ import NodeWithGraphView from './NodeWithGraphView.svelte';
17
+ import NodeWithTableView from './NodeWithTableView.svelte';
18
  import NodeSearch from './NodeSearch.svelte';
19
  import '@xyflow/svelte/dist/style.css';
20
 
 
22
 
23
  const nodeTypes: NodeTypes = {
24
  basic: NodeWithParams,
25
+ graph_view: NodeWithGraphView,
26
+ table_view: NodeWithTableView,
27
  };
28
 
29
+ const nodes = writable<Node[]>([]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  const edges = writable<Edge[]>([
32
  {
web/src/LynxKiteNode.svelte CHANGED
@@ -56,12 +56,14 @@
56
  }
57
  .node-container {
58
  padding: 8px;
59
- min-width: 170px;
60
- max-width: 300px;
61
  }
62
  .lynxkite-node {
63
  box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
64
  background: white;
 
 
 
 
65
  }
66
  .title {
67
  background: #ff8800;
 
56
  }
57
  .node-container {
58
  padding: 8px;
 
 
59
  }
60
  .lynxkite-node {
61
  box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
62
  background: white;
63
+ min-width: 170px;
64
+ max-width: 300px;
65
+ max-height: 400px;
66
+ overflow-y: auto;
67
  }
68
  .title {
69
  background: #ff8800;
web/src/{NodeWithGraphVisualization.svelte → NodeWithGraphView.svelte} RENAMED
@@ -5,14 +5,13 @@
5
  import * as graphologyLibrary from 'graphology-library';
6
  import LynxKiteNode from './LynxKiteNode.svelte';
7
  type $$Props = NodeProps;
8
- export let id: $$Props['id'];
9
  export let data: $$Props['data'];
10
  let sigmaCanvas: HTMLElement;
11
  let sigmaInstance: Sigma;
12
 
13
  $: if (sigmaCanvas) sigmaInstance = new Sigma(new graphology.Graph(), sigmaCanvas);
14
- $: if (sigmaInstance && data.graph) {
15
- const graph = graphology.Graph.from(data.graph);
16
  graphologyLibrary.layout.random.assign(graph);
17
  const settings = graphologyLibrary.layoutForceAtlas2.inferSettings(graph);
18
  graphologyLibrary.layoutForceAtlas2.assign(graph, { iterations: 10, settings });
 
5
  import * as graphologyLibrary from 'graphology-library';
6
  import LynxKiteNode from './LynxKiteNode.svelte';
7
  type $$Props = NodeProps;
 
8
  export let data: $$Props['data'];
9
  let sigmaCanvas: HTMLElement;
10
  let sigmaInstance: Sigma;
11
 
12
  $: if (sigmaCanvas) sigmaInstance = new Sigma(new graphology.Graph(), sigmaCanvas);
13
+ $: if (sigmaInstance && data.view) {
14
+ const graph = graphology.Graph.from(data.view);
15
  graphologyLibrary.layout.random.assign(graph);
16
  const settings = graphologyLibrary.layoutForceAtlas2.inferSettings(graph);
17
  graphologyLibrary.layoutForceAtlas2.assign(graph, { iterations: 10, settings });
web/src/NodeWithTableView.svelte ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { type NodeProps } from '@xyflow/svelte';
3
+ import LynxKiteNode from './LynxKiteNode.svelte';
4
+ type $$Props = NodeProps;
5
+ export let data: $$Props['data'];
6
+ </script>
7
+
8
+ <LynxKiteNode {...$$props}>
9
+ {#if data.view}
10
+ {#each Object.entries(data.view.dataframes) as [name, df]}
11
+ <div class="df-head">{name}</div>
12
+ <table>
13
+ <tr>
14
+ {#each df.columns as column}
15
+ <th>{column}</th>
16
+ {/each}
17
+ </tr>
18
+ {#each df.data as row}
19
+ <tr>
20
+ {#each row as cell}
21
+ <td>{cell}</td>
22
+ {/each}
23
+ </tr>
24
+ {/each}
25
+ </table>
26
+ {/each}
27
+ {/if}
28
+ </LynxKiteNode>
29
+ <style>
30
+ .df-head {
31
+ font-weight: bold;
32
+ padding: 8px;
33
+ background: #f0f0f0;
34
+ }
35
+ table {
36
+ margin: 8px;
37
+ border-collapse: collapse;
38
+ }
39
+ </style>