Skip to content

Pajek read/write: store and read tabular data as attributes#295

Open
janezd wants to merge 1 commit into
biolab:masterfrom
janezd:read-write-attributes
Open

Pajek read/write: store and read tabular data as attributes#295
janezd wants to merge 1 commit into
biolab:masterfrom
janezd:read-write-attributes

Conversation

@janezd

@janezd janezd commented May 22, 2026

Copy link
Copy Markdown
Contributor
Issue

Fixes #286. Fixes #275.

Description of changes

When saving, add data about nodes and edges. For nodes, this data is saved in the same format as in NetworkX, which is a hack of what's in Pajek. For edges, the format is the same, though unsupported by NetworkX (AFAIK).

When reading -- read the data save in this way.

The format is a mess: it allows (but doesn't require) node coordinates. Edges may have a numeric weight or a non-numeric label, but not both, to which we now add NetworkX-like key-value pairs, and so forth.

Tests for existing mess are lacking, and the new tests might not cover all possible combinations.

Includes
  • Code changes
  • Tests
  • Documentation

@janezd janezd force-pushed the read-write-attributes branch 2 times, most recently from 8340d91 to 3151ddc Compare May 25, 2026 12:05
@janezd janezd requested a review from Copilot May 25, 2026 14:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the Pajek (.net) import/export support to preserve tabular node/edge data as key–value attributes (in a NetworkX-like style) and updates the Save/Load widgets and tests to exercise the new behavior.

Changes:

  • Enhance Pajek reader/writer to parse and emit node/edge attribute key–value pairs, producing Orange Table-backed node/edge data when present.
  • Update Save Network widget UI to allow choosing node/edge label variables and optionally appending remaining attributes on save.
  • Add/extend tests and test fixtures for quoted labels and attribute round-tripping.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
orangecontrib/network/widgets/OWNxSave.py Adds UI/settings for node/edge label selection and optional attribute appending when saving.
orangecontrib/network/widgets/OWNxFile.py Adjusts node data output behavior when the underlying network already stores nodes as a Table.
orangecontrib/network/network/tests/towns.net Quotes a multi-word edge label to match new shlex parsing behavior.
orangecontrib/network/network/tests/towns-2.net New fixture covering node + edge attributes and mixed edge value/attributes cases.
orangecontrib/network/network/tests/test_readwrite.py Adds tests for reading/writing attributes and helper utilities.
orangecontrib/network/network/readwrite.py Implements parsing/writing of vertex/edge attributes and table conversion helpers.
i18n/si/msgs.jaml Adds translation keys for newly introduced UI strings and read/write formatting strings.
Comments suppressed due to low confidence (5)

orangecontrib/network/widgets/OWNxSave.py:112

  • In set_network, the edge-label setup uses network.edges[0] without checking that network.edges is non-empty (Pajek files can contain vertices only), which can raise IndexError when a network has no edges.
        if isinstance(network.edges[0].edge_data, Table):
            self.controls.edge_variable.setEnabled(True)

orangecontrib/network/widgets/OWNxSave.py:115

  • domain = network.edges.domain is invalid because network.edges is a list; this will raise AttributeError. The domain should come from the edge data table (e.g. network.edges[0].edge_data.domain).
            self.controls.edge_variable.setEnabled(True)
            domain = network.edges.domain
            self.edge_model.set_domain(domain)
            for attr in domain.metas:

orangecontrib/network/widgets/OWNxSave.py:121

  • The hint restoration logic is broken: it does domain[self.edge_variable] (where self.edge_variable is a Variable, not a name) and then checks it against self.edge_model. This will either error or never match; it should look up domain[self.edge_variable_hint] (and also verify the variable is in self.edge_model).
            if self.edge_variable_hint in domain and \
                    (attr := domain[self.edge_variable]) in self.edge_model:
                self.edge_variable = attr

orangecontrib/network/widgets/OWNxSave.py:149

  • dict_rows_from_table(..., self.label_variable) / (..., self.edge_variable) passes a Variable object into the exclude parameter, but dict_rows_from_table compares attr.name != exclude (string vs Variable), so the selected label column will not be excluded and will be duplicated in the saved attributes. Pass the variable name (or extend dict_rows_from_table to accept a Variable).
            if self.append_node_data and isinstance(data := net.nodes, Table):
                label_data = dict_rows_from_table(data, self.label_variable)
            else:
                label_data = None
            if self.append_edge_data and isinstance(data := net.edges[0].edge_data, Table):
                edge_data = dict_rows_from_table(data, self.edge_variable)
            else:

orangecontrib/network/widgets/OWNxSave.py:117

  • Default label auto-detection only looks for meta variables named node_label / edge_label, but this PR’s Pajek reader/writer uses node label / edge label (with spaces). This prevents the widget from auto-selecting the correct label variable for Pajek-loaded networks; consider accepting both naming variants.
            for attr in domain.metas:
                if attr.name == "node_label":
                    self.label_variable = attr
                    break
            if self.label_variable_hint in domain and \
                    (attr := domain[self.label_variable_hint]) in self.label_model:
                self.label_variable = domain[self.label_variable_hint]
        else:
            self.label_model.set_domain(None)
            self.label_variable = None
            self.controls.label_variable.setEnabled(False)

        if isinstance(network.edges[0].edge_data, Table):
            self.controls.edge_variable.setEnabled(True)
            domain = network.edges.domain
            self.edge_model.set_domain(domain)
            for attr in domain.metas:
                if attr.name == "edge_label":
                    self.edge_variable = attr

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread orangecontrib/network/widgets/OWNxSave.py Outdated
Comment thread orangecontrib/network/widgets/OWNxSave.py Outdated
Comment thread orangecontrib/network/widgets/OWNxFile.py
Comment thread orangecontrib/network/network/readwrite.py
@janezd janezd force-pushed the read-write-attributes branch from 3151ddc to 0ec2e3e Compare May 26, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid node labels and missing edge labels in saved Pajek data Save Network: save the same as in NetworkX

2 participants