Skip to content

Commit

Permalink
Add Node.nodegroup_root #11613
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls committed Nov 11, 2024
1 parent cf68f62 commit cb99670
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 4 deletions.
21 changes: 18 additions & 3 deletions arches/app/models/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ def check_default_configs(default_configs, configs):

self.populate_null_nodegroups()

@classmethod
def check(cls, **kwargs):
"""Bypass GraphModel.check()."""
return super(models.GraphModel, cls).check(**kwargs)

def refresh_from_database(self):
"""
Updates card, edge, and node data from the database, bypassing the
Expand Down Expand Up @@ -344,6 +349,7 @@ def add_node(self, node, nodegroups=None):
node.ontologyclass = nodeobj.get("ontologyclass", "")
node.datatype = nodeobj.get("datatype", "")
node.nodegroup_id = nodeobj.get("nodegroup_id", "")
node.nodegroup_root_id = nodeobj.get("nodegroup_root_id", node.nodegroup_id)
node.config = nodeobj.get("config", None)
node.issearchable = nodeobj.get("issearchable", True)
node.isrequired = nodeobj.get("isrequired", False)
Expand All @@ -367,6 +373,7 @@ def add_node(self, node, nodegroups=None):
node.nodegroup = self.get_or_create_nodegroup(
nodegroupid=node.nodegroup_id
)
node.nodegroup_root_id = node.nodegroup_id
if nodegroups is not None and str(node.nodegroup_id) in nodegroups:
node.nodegroup.cardinality = nodegroups[str(node.nodegroup_id)][
"cardinality"
Expand All @@ -379,6 +386,7 @@ def add_node(self, node, nodegroups=None):
]["parentnodegroup_id"]
else:
node.nodegroup = None
node.nodegroup_root = None

node.graph = self

Expand Down Expand Up @@ -608,6 +616,7 @@ def save(self, validate=True, nodeid=None):

if nodeid is not None:
node = self.nodes[nodeid]
node.nodegroup_root_id = node.nodegroup_id
branch_publication_id = node.sourcebranchpublication_id
self.update_es_node_mapping(node, datatype_factory, se)
self.create_node_alias(node)
Expand Down Expand Up @@ -636,6 +645,7 @@ def save(self, validate=True, nodeid=None):

else:
for node in self.nodes.values():
node.nodegroup_root_id = node.nodegroup_id
self.update_es_node_mapping(node, datatype_factory, se)
node.save()

Expand Down Expand Up @@ -1283,7 +1293,8 @@ def update_node(self, node):
new_node.fieldname = node["fieldname"]
self.populate_null_nodegroups()

# new_node will always have a nodegroup id even it if was set to None becuase populate_null_nodegroups
# new_node will always have a nodegroup id even if if was set to None
# because populate_null_nodegroups
# will populate the nodegroup id with the parent nodegroup
# add/remove a card if a nodegroup was added/removed
if new_node.nodegroup_id != old_node.nodegroup_id:
Expand Down Expand Up @@ -1316,7 +1327,7 @@ def update_node(self, node):

def delete_node(self, node=None):
"""
deletes a node and all if it's children from a graph
deletes a node and all of its children from a graph
Arguments:
node -- a node id or Node model to delete from the graph
Expand Down Expand Up @@ -2479,7 +2490,7 @@ def _update_source_nodegroup_hierarchy(nodegroup):
# graph ( the graph mapped to `self` ); we iterate over the item attributes and map
# them to source item. If the item does not have a `source_identifier` attribute, it
# has been newly created; we update the `graph_id` to match the source graph. We are
# not saving in this block so updates can accur in any order.
# not saving in this block so updates can occur in any order.
for future_widget in list(editable_future_graph.widgets.values()):
source_widget = future_widget.source_identifier

Expand Down Expand Up @@ -2636,6 +2647,7 @@ def _update_source_nodegroup_hierarchy(nodegroup):
"graph_id",
"nodeid",
"nodegroup_id",
"nodegroup_root_id",
"source_identifier_id",
"is_collector",
]:
Expand All @@ -2649,6 +2661,9 @@ def _update_source_nodegroup_hierarchy(nodegroup):
source_node.nodegroup_id = (
future_node_nodegroup_node.source_identifier_id
)
source_node.nodegroup_root_id = (
future_card_nodegroup_node.source_identifier_id
)

self.nodes[source_node.pk] = source_node
else: # newly-created node
Expand Down
35 changes: 35 additions & 0 deletions arches/app/models/migrations/10438_node_nodegroup_root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 5.1.3 on 2024-11-11 07:27

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("models", "10437_node_alias_not_null"),
]

def set_nodegroup_root(apps, schema_editor):
Node = apps.get_model("models", "Node")
all_but_top_nodes = Node.objects.exclude(istopnode=True)
for node in all_but_top_nodes:
assert node.nodegroup_id is not None, f"Missing nodegroup for {node!r}"
node.nodegroup_root_id = node.nodegroup_id
Node.objects.bulk_update(all_but_top_nodes, ["nodegroup_root_id"])

operations = [
migrations.AddField(
model_name="node",
name="nodegroup_root",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="sibling_nodes",
related_query_name="sibling_node",
to="models.node",
),
),
migrations.RunPython(set_nodegroup_root, migrations.RunPython.noop),
]
33 changes: 33 additions & 0 deletions arches/app/models/migrations/10439_add_node_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 5.1.3 on 2024-11-11 07:53

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("models", "10438_node_nodegroup_root"),
]

operations = [
migrations.AddConstraint(
model_name="node",
constraint=models.CheckConstraint(
condition=models.Q(
("istopnode", True), ("nodegroup__isnull", False), _connector="OR"
),
name="has_nodegroup_or_istopnode",
),
),
migrations.AddConstraint(
model_name="node",
constraint=models.CheckConstraint(
condition=models.Q(
("istopnode", True),
("nodegroup_root__isnull", False),
_connector="OR",
),
name="has_nodegroup_root_or_istopnode",
),
),
]
16 changes: 16 additions & 0 deletions arches/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,14 @@ def __init__(self, *args, **kwargs):
graph = models.ForeignKey(
GraphModel, db_column="graphid", blank=True, null=True, on_delete=models.CASCADE
)
nodegroup_root = models.ForeignKey(
"self",
blank=True,
null=True,
on_delete=models.CASCADE,
related_name="sibling_nodes",
related_query_name="sibling_node",
)
config = I18n_JSONField(blank=True, null=True, db_column="config")
issearchable = models.BooleanField(default=True)
isrequired = models.BooleanField(default=False)
Expand Down Expand Up @@ -928,6 +936,14 @@ class Meta:
models.UniqueConstraint(
fields=["alias", "graph"], name="unique_alias_graph"
),
models.CheckConstraint(
condition=Q(istopnode=True) | Q(nodegroup__isnull=False),
name="has_nodegroup_or_istopnode",
),
models.CheckConstraint(
condition=Q(istopnode=True) | Q(nodegroup_root__isnull=False),
name="has_nodegroup_root_or_istopnode",
),
]


Expand Down
1 change: 1 addition & 0 deletions releases/8.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Arches 8.0.0 Release Notes
- Add session-based REST APIs for login, logout [#11261](https://github.com/archesproject/arches/issues/11261)
- Add system check advising next action when enabling additional languages without updating graphs [#10079](https://github.com/archesproject/arches/issues/10079)
- Improve handling of longer model names [#11317](https://github.com/archesproject/arches/issues/11317)
- New column `Node.nodegroup_root`: self-referring foreign key to the root node for its nodegroup [#11613](https://github.com/archesproject/arches/issues/11613)
- Support more expressive plugin URLs [#11320](https://github.com/archesproject/arches/issues/11320)
- Make node aliases not nullable [#10437](https://github.com/archesproject/arches/issues/10437)
- Concepts API no longer responds with empty body for error conditions [#11519](https://github.com/archesproject/arches/issues/11519)
Expand Down
4 changes: 3 additions & 1 deletion tests/models/graph_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import uuid

from django.contrib.auth.models import User
from tests.base_test import ArchesTestCase
from arches.app.models import models
from arches.app.models.graph import Graph, GraphValidationError
Expand Down Expand Up @@ -120,6 +119,7 @@ def setUpTestData(cls):
"istopnode": True,
"name": "Node",
"nodegroup_id": "20000000-0000-0000-0000-100000000001",
"nodegroup_root_id": "20000000-0000-0000-0000-100000000001",
"nodeid": "20000000-0000-0000-0000-100000000001",
"ontologyclass": "http://www.cidoc-crm.org/cidoc-crm/E1_CRM_Entity",
},
Expand All @@ -133,6 +133,7 @@ def setUpTestData(cls):
"istopnode": False,
"name": "Node Type",
"nodegroup_id": "20000000-0000-0000-0000-100000000001",
"nodegroup_root_id": "20000000-0000-0000-0000-100000000001",
"nodeid": "20000000-0000-0000-0000-100000000002",
"ontologyclass": "http://www.cidoc-crm.org/cidoc-crm/E55_Type",
},
Expand Down Expand Up @@ -1280,6 +1281,7 @@ def test_update_empty_graph_from_editable_future_graph(self):
if key not in [
"graph_id",
"nodegroup_id",
"nodegroup_root_id",
"nodeid",
"source_identifier_id",
]:
Expand Down
3 changes: 3 additions & 0 deletions tests/models/resource_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,15 +489,18 @@ def test_self_referring_resource_instance_descriptor(self):
graph = Graph.new(name="Self-referring descriptor test", is_resource=True)
nodegroup = models.NodeGroup.objects.create()
string_node = models.Node.objects.create(
pk=nodegroup.pk,
graph=graph,
nodegroup=nodegroup,
nodegroup_root_id=nodegroup.pk,
name="String Node",
datatype="string",
istopnode=False,
)
resource_instance_node = models.Node.objects.create(
graph=graph,
nodegroup=nodegroup,
nodegroup_root_id=nodegroup.pk,
name="Resource Node",
datatype="resource-instance",
istopnode=False,
Expand Down
2 changes: 2 additions & 0 deletions tests/models/tile_model_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,10 +732,12 @@ def test_check_for_missing_nodes(self):
pk=UUID("41111111-0000-0000-0000-000000000000")
)
required_file_list_node = Node(
pk=node_group.pk,
graph=graph,
name="Required file list",
datatype="file-list",
nodegroup=node_group,
nodegroup_root_id=node_group.pk,
isrequired=True,
istopnode=False,
)
Expand Down
1 change: 1 addition & 0 deletions tests/views/graph_manager_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def setUpTestData(cls):
"istopnode": False,
"name": "Node Type",
"nodegroup_id": "20000000-0000-0000-0000-100000000001",
"nodegroup_root_id": "20000000-0000-0000-0000-100000000001",
"nodeid": "20000000-0000-0000-0000-100000000002",
"ontologyclass": "http://www.cidoc-crm.org/cidoc-crm/E55_Type",
},
Expand Down

0 comments on commit cb99670

Please sign in to comment.