Skip to content

Commit

Permalink
talipot-python: Add a new Trvial Graph Format import plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
anlambert committed Nov 16, 2023
1 parent db6c600 commit fdfc457
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 0 deletions.
80 changes: 80 additions & 0 deletions library/talipot-python/plugins/import/TrivialGraphFormatImport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (C) 2023 The Talipot developers
#
# Talipot is a fork of Tulip, created by David Auber
# and the Tulip development Team from LaBRI, University of Bordeaux
#
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information

# That plugin imports a graph from a file in the Trivial Graph Format (TGF)
# (see https://en.wikipedia.org/wiki/Trivial_Graph_Format)

import talipotplugins

from talipot import tlp


class TrivialGraphFormatImport(tlp.ImportModule):
def __init__(self, context):
tlp.ImportModule.__init__(self, context)
self.addFileParameter("filename", True, "The TGF file to import")

def fileExtensions(self):
return ["tgf"]

def parseTGF(self, tgfFile):
parsingEdges = False
nodesMap = {}
for line in tgfFile:
line = line.rstrip()
if not line:
continue
if line.startswith("#"):
parsingEdges = True
continue
if not parsingEdges:
split = line.split(" ", maxsplit=1)
n = self.graph.addNode()
nodesMap[split[0]] = n
self.graph["viewLabel"][n] = split[1] if len(split) > 1 else split[0]

else:
split = line.split(" ", maxsplit=2)
if len(split) < 2:
raise ValueError(f"Invalid edge definition: {line}")
for i in range(2):
if split[i] not in nodesMap:
raise ValueError(
f"Invalid node identifier found when parsing edges: {split[i]}"
)
e = self.graph.addEdge(nodesMap[split[0]], nodesMap[split[1]])
if len(split) > 2:
self.graph["viewLabel"][e] = split[2]

def importGraph(self):
# parse the TGF file
with open(self.dataSet["filename"], "r") as tgfFile:
self.parseTGF(tgfFile)

return True


pluginDoc = """
<p>Supported extension: tgf</p><p>Imports a graph from a file in the
Trivial Graph Format (hhttps://en.wikipedia.org/wiki/Trivial_Graph_Format).
The format consists of a list of node definitions, which map node IDs to labels,
followed by a list of edges, which specify node pairs and an optional edge label.</p>
"""

# The line below does the magic to register the plugin to the plugin database
# and updates the GUI to make it accessible through the menus.
talipotplugins.registerPluginOfGroup(
"TrivialGraphFormatImport",
"Trivial Graph Format (TGF)",
"Antoine Lambert",
"16/11/2023",
pluginDoc,
"1.0",
"File",
)
1 change: 1 addition & 0 deletions tests/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ UNIT_TEST(PythonGraphStructureTest test_graph_structure)
UNIT_TEST(PythonGraphPropertiesTest test_graph_properties)
UNIT_TEST(PythonPluginParametersTest test_plugin_registration_and_parameters)
UNIT_TEST(PythonTalipotPluginsTest test_talipot_plugins)
UNIT_TEST(PythonTGFImportTest test_tgf_import)
128 changes: 128 additions & 0 deletions tests/python/test_tgf_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright (C) 2023 The Talipot developers
#
# Talipot is a fork of Tulip, created by David Auber
# and the Tulip development Team from LaBRI, University of Bordeaux
#
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information

import os
import tempfile
import textwrap
import unittest

from talipot import tlp


class TestTalipotPlugins(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.tgfPluginName = "Trivial Graph Format (TGF)"
if "TALIPOT_BUILD_DIR" in os.environ:
talipot_build_dir = os.environ["TALIPOT_BUILD_DIR"]
if talipot_build_dir:
tlp.loadPluginsFromDir(
os.path.join(talipot_build_dir, "library/talipot-python")
)

def test_tgf_plugin_loaded(self):
self.assertTrue("Trivial Graph Format (TGF)" in tlp.getImportPluginsList())

def test_load_tgf_graph_no_labels(self):
with tempfile.NamedTemporaryFile() as tgfFile:
with open(tgfFile.name, "w") as tgfFileWriter:
tgfFileWriter.write(
textwrap.dedent(
"""
0
1
2
3
#
0 1
0 2
1 2
1 3
3 0
"""
)
)
graph = tlp.importGraph(self.tgfPluginName, {"filename": tgfFile.name})
self.assertTrue(graph is not None)
self.assertEqual(graph.numberOfNodes(), 4)
self.assertEqual(graph.numberOfEdges(), 5)
nodes = graph.nodes()
self.assertTrue(graph.existEdge(nodes[0], nodes[1]))
self.assertTrue(graph.existEdge(nodes[0], nodes[2]))
self.assertTrue(graph.existEdge(nodes[1], nodes[2]))
self.assertTrue(graph.existEdge(nodes[1], nodes[3]))
self.assertTrue(graph.existEdge(nodes[3], nodes[0]))
self.assertEqual(
[graph["viewLabel"][n] for n in graph.nodes()], ["0", "1", "2", "3"]
)

def test_load_tgf_graph_with_labels(self):
with tempfile.NamedTemporaryFile() as tgfFile:
with open(tgfFile.name, "w") as tgfFileWriter:
tgfFileWriter.write(
textwrap.dedent(
"""
0 Node 0
1 Node 1
2 Node 2
3 Node 3
#
0 1 Edge 0
0 2 Edge 1
1 2 Edge 2
1 3 Edge 3
3 0 Edge 4
"""
)
)
graph = tlp.importGraph(self.tgfPluginName, {"filename": tgfFile.name})
self.assertEqual(
[graph["viewLabel"][n] for n in graph.nodes()],
[f"Node {i}" for i in range(4)],
)
self.assertEqual(
[graph["viewLabel"][e] for e in graph.edges()],
[f"Edge {i}" for i in range(5)],
)

def test_load_tgf_graph_invalid_edge(self):
with tempfile.NamedTemporaryFile() as tgfFile:
with open(tgfFile.name, "w") as tgfFileWriter:
tgfFileWriter.write(
textwrap.dedent(
"""
0
1
2
3
#
4
"""
)
)
graph = tlp.importGraph(self.tgfPluginName, {"filename": tgfFile.name})
self.assertTrue(graph is None)

def test_load_tgf_graph_invalid_node_id(self):
with tempfile.NamedTemporaryFile() as tgfFile:
with open(tgfFile.name, "w") as tgfFileWriter:
tgfFileWriter.write(
textwrap.dedent(
"""
0
1
2
3
#
4 5
"""
)
)
graph = tlp.importGraph(self.tgfPluginName, {"filename": tgfFile.name})
self.assertTrue(graph is None)

0 comments on commit fdfc457

Please sign in to comment.