Skip to content

Commit

Permalink
Collect nodes using controlled lists #10554
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls authored and chiatt committed Feb 28, 2024
1 parent 347d7af commit 105a33d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 7 deletions.
24 changes: 24 additions & 0 deletions arches/app/models/migrations/10553_lists_reffed_by_node_idx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.10 on 2024-02-23 08:33

from django.db import migrations, models
import django.db.models.fields.json
import django.db.models.functions.comparison


class Migration(migrations.Migration):
dependencies = [
("models", "10552_add_reference_datatype"),
]

operations = [
migrations.AddIndex(
model_name="node",
index=models.Index(
django.db.models.functions.comparison.Cast(
django.db.models.fields.json.KeyTextTransform("controlledList", "config"),
output_field=models.UUIDField(null=True)
),
name="lists_reffed_by_node_idx",
),
),
]
8 changes: 8 additions & 0 deletions arches/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from arches.app.utils import import_class_from_string
from django.contrib.gis.db import models
from django.db.models import Deferrable, JSONField
from django.db.models.fields.json import KT
from django.db.models.functions import Cast
from django.core.cache import caches
from django.core.exceptions import ValidationError
from django.core.mail import EmailMultiAlternatives
Expand Down Expand Up @@ -725,6 +727,12 @@ class Meta:
models.UniqueConstraint(fields=["name", "nodegroup"], name="unique_nodename_nodegroup"),
models.UniqueConstraint(fields=["alias", "graph"], name="unique_alias_graph"),
]
indexes = [
models.Index(
Cast(KT("config__controlledList"), output_field=models.UUIDField(null=True)),
name="lists_reffed_by_node_idx",
)
]


@receiver(post_save, sender=Node)
Expand Down
36 changes: 29 additions & 7 deletions arches/app/views/controlled_lists.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from collections import defaultdict
from datetime import datetime

from django.contrib.postgres.expressions import ArraySubquery
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.db.models import Max
from django.db.models import Max, OuterRef, UUIDField
from django.db.models.fields.json import KT
from django.db.models.functions import Cast
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.utils.translation import get_language, gettext as _
Expand All @@ -13,19 +16,20 @@
ControlledListItem,
ControlledListItemLabel,
Language,
Node,
)
from arches.app.utils.betterJSONSerializer import JSONDeserializer
from arches.app.utils.decorators import group_required
from arches.app.utils.response import JSONErrorResponse, JSONResponse
from arches.app.utils.string_utils import str_to_bool


def serialize(obj, depth_map=None, flat=False):
def serialize(obj, depth_map=None, flat=False, nodes=False):
if depth_map is None:
depth_map = defaultdict(int)
match obj:
case ControlledList():
return {
data = {
"id": str(obj.id),
"name": obj.name,
"dynamic": obj.dynamic,
Expand All @@ -38,6 +42,9 @@ def serialize(obj, depth_map=None, flat=False):
key=lambda d: d["sortorder"],
),
}
if nodes:
data["nodes"] = [str(uid) for uid in obj.node_ids]
return data
case ControlledListItem():
if obj.parent_id:
depth_map[obj.id] = depth_map[obj.parent_id] + 1
Expand Down Expand Up @@ -134,12 +141,27 @@ def handle_item(itemDict):
class ControlledListsView(View):
def get(self, request):
"""Returns either a flat representation (?flat=true) or a tree (default)."""
lists = (
ControlledList.objects.all()
.annotate(
node_ids=ArraySubquery(
Node.objects.annotate(
as_uuid=Cast(
KT("config__controlledList"), output_field=UUIDField()
)
)
.filter(as_uuid=OuterRef("id"))
.values("pk")
)
)
# check node perms?
.order_by("name")
.prefetch_related(*prefetch_terms(request))
)
data = {
"controlled_lists": [
serialize(obj, flat=str_to_bool(request.GET.get("flat", "false")))
for obj in ControlledList.objects.all()
.order_by("name")
.prefetch_related(*prefetch_terms(request))
serialize(obj, nodes=True, flat=str_to_bool(request.GET.get("flat", "false")))
for obj in lists
],
}

Expand Down
19 changes: 19 additions & 0 deletions tests/views/controlled_lists_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
ControlledListItemLabel,
DValueType,
Language,
Node,
NodeGroup,
)
from arches.app.views.controlled_lists import serialize
from tests.base_test import ArchesTestCase
Expand Down Expand Up @@ -102,6 +104,20 @@ def setUpTestData(cls):
]
)

random_node_group = NodeGroup.objects.last()
cls.node_using_list1 = Node(
pk="a3c5b7d3-ef2c-4f8b-afd5-f8d4636b8834",
name="Uses list1",
datatype="reference",
nodegroup=random_node_group,
istopnode=False,
config={
"multiValue": False,
"controlledList": str(cls.list1.pk),
},
)
cls.node_using_list1.save()

def test_get_controlled_lists(self):
self.client.force_login(self.anonymous)
response = self.client.get(reverse("controlled_lists"))
Expand Down Expand Up @@ -129,6 +145,9 @@ def test_get_controlled_lists(self):
# pprint(result)

first_list, second_list = result["controlled_lists"]

self.assertEqual(first_list["nodes"], [str(self.node_using_list1.pk)])

for item in first_list["items"]:
self.assertEqual(item["children"], [])

Expand Down

0 comments on commit 105a33d

Please sign in to comment.