Skip to content

Commit

Permalink
Merge branch 'main' into jtw/primevue-4
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls authored Jul 30, 2024
2 parents b99a778 + db5e7f0 commit 261711f
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added compatibility with PrimeVue 4 [#16](https://github.com/archesproject/arches-references/pull/16)

### Changed
- Generate URIs when not supplied [#17](https://github.com/archesproject/arches-references/pull/17)

### Fixed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.14 on 2024-07-30 16:09

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("arches_references", "0003_add_node_index"),
]

operations = [
migrations.AlterField(
model_name="listitem",
name="uri",
field=models.URLField(blank=True, max_length=2048),
),
]
47 changes: 30 additions & 17 deletions arches_references/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@
import uuid
from collections import defaultdict

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from django.db import models, transaction
from django.db.models import Deferrable, Q
from django.utils.translation import gettext_lazy as _

from arches.app.models.models import DValueType, Language, Node
from arches.app.models.utils import field_names
from arches_references.querysets import (
ListQuerySet,
ListItemImageManager,
ListItemValueQuerySet,
NodeQuerySet,
)
from arches_references.utils import field_names


class List(models.Model):
Expand Down Expand Up @@ -124,7 +125,7 @@ def bulk_update_item_parentage_and_order(self, parent_map, sortorder_map):

class ListItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
uri = models.URLField(max_length=2048, null=True, blank=True)
uri = models.URLField(max_length=2048, null=False, blank=True)
list = models.ForeignKey(
List,
on_delete=models.CASCADE,
Expand Down Expand Up @@ -157,15 +158,32 @@ class Meta:
),
]

def clean_fields(self, exclude=None):
super().clean_fields(exclude=exclude)
if not self.id and "id" not in exclude:
id_field = [f for f in self._meta.fields if f.name == "id"][0]
self.id = id_field.get_default()

def clean(self):
if not self.uri:
self.uri = self.generate_uri()

def generate_uri(self):
"""Similar logic exists in `etl_collections_to_controlled_lists` migration."""
if not self.id:
raise RuntimeError("URI generation attempted without a primary key.")

parts = [settings.PUBLIC_SERVER_ADDRESS.rstrip("/")]
if settings.FORCE_SCRIPT_NAME:
parts.append(settings.FORCE_SCRIPT_NAME)
parts += ["plugins", "controlled-list-manager", "item", str(self.id)]

return "/".join(parts)

def ensure_pref_label(self):
if not self.list_item_values.filter(valuetype="prefLabel").exists():
raise ValidationError(_("At least one preferred label is required."))

def clean_fields(self, exclude=None):
super().clean_fields(exclude=exclude)
if (not exclude or "uri" not in exclude) and not self.uri:
self.uri = None

def serialize(self, depth_map=None, flat=False):
if depth_map is None:
depth_map = defaultdict(int)
Expand Down Expand Up @@ -260,15 +278,10 @@ def serialize(self):
}

def delete(self):
msg = _("Deleting the item's only remaining preferred label is not permitted.")
if (
self.valuetype_id == "prefLabel"
and len(self.list_item.list_item_values.filter(valuetype_id="prefLabel"))
< 2
):
raise ValidationError(msg)

return super().delete()
with transaction.atomic():
ret = super().delete()
self.list_item.ensure_pref_label()
return ret


class ListItemImage(models.Model):
Expand Down
2 changes: 0 additions & 2 deletions arches_references/utils.py

This file was deleted.

33 changes: 20 additions & 13 deletions arches_references/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _

from arches.app.models.utils import field_names
from arches.app.utils.betterJSONSerializer import JSONDeserializer
from arches.app.utils.decorators import group_required
from arches.app.utils.permission_backend import get_nodegroups_by_perm
Expand All @@ -23,7 +24,6 @@
ListItemValue,
NodeProxy,
)
from arches_references.utils import field_names

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -186,19 +186,26 @@ def post(self, request):
return JSONErrorResponse(status=HTTPStatus.BAD_REQUEST)

try:
with transaction.atomic():
controlled_list = (
List.objects.filter(pk=list_id)
.annotate(max_sortorder=Max("list_items__sortorder", default=-1))
.get()
)
item = ListItem.objects.create(
list=controlled_list,
sortorder=controlled_list.max_sortorder + 1,
parent_id=parent_id,
)
controlled_list = (
List.objects.filter(pk=list_id)
.annotate(max_sortorder=Max("list_items__sortorder", default=-1))
.get()
)
except List.DoesNotExist:
return JSONErrorResponse(status=HTTPStatus.NOT_FOUND)
return JSONErrorResponse(status=HTTPStatus.BAD_REQUEST)

try:
item = ListItem(
list=controlled_list,
sortorder=controlled_list.max_sortorder + 1,
parent_id=parent_id,
)
item.full_clean()
item.save()
except ValidationError as ve:
return JSONErrorResponse(
message="\n".join(ve.messages), status=HTTPStatus.BAD_REQUEST
)

return JSONResponse(item.serialize(), status=HTTPStatus.CREATED)

Expand Down
19 changes: 19 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.test import TestCase

from arches_references.models import ListItem

# these tests can be run from the command line via
# python manage.py test tests.test_models --settings="tests.test_settings"


class ListItemTests(TestCase):
def test_uri_generation_guards_against_failure(self):
# Don't bother setting up a list.
item = ListItem(sortorder=0)
item.id = None

with self.assertRaises(RuntimeError):
item.clean()

item.full_clean(exclude={"list"})
self.assertIsNotNone(item.uri)
8 changes: 6 additions & 2 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from django.contrib.auth.models import Group, User
from django.test import TestCase
from django.test.utils import override_settings
from django.urls import reverse
from guardian.shortcuts import assign_perm

Expand Down Expand Up @@ -391,7 +392,8 @@ def test_recursive_cycles(self):
)
self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST, response.content)

def test_update_uri_blank(self):
@override_settings(PUBLIC_SERVER_ADDRESS="public/", FORCE_SCRIPT_NAME="script")
def test_generate_uri(self):
self.client.force_login(self.admin)
item = self.list1.list_items.first()

Expand All @@ -402,7 +404,9 @@ def test_update_uri_blank(self):
)
self.assertEqual(response.status_code, HTTPStatus.NO_CONTENT, response.content)
item.refresh_from_db()
self.assertIsNone(item.uri)
self.assertEqual(
item.uri, f"public/script/plugins/controlled-list-manager/item/{item.pk}"
)

def test_delete_list_item(self):
self.client.force_login(self.admin)
Expand Down

0 comments on commit 261711f

Please sign in to comment.