Skip to content

Commit

Permalink
[Fixes #11579] Use autocomplete API for editing linked resources
Browse files Browse the repository at this point in the history
  • Loading branch information
etj committed Oct 12, 2023
1 parent fbee36c commit e6a8ef8
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 110 deletions.
44 changes: 28 additions & 16 deletions geonode/base/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
import html
import json
import logging

from django.db.models.query import QuerySet
from bootstrap3_datetime.widgets import DateTimePicker
from dal import autocomplete
import dal.forward
from django import forms
from django.conf import settings
from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -347,35 +349,45 @@ def _get_thesauro_title_label(item, lang):


class LinkedResourceForm(forms.ModelForm):
linked_resources = forms.MultipleChoiceField(label=_("Link to"), required=False)
linked_resources = forms.ModelMultipleChoiceField(
label=_("Related resources"),
required=False,
queryset=None,
widget=autocomplete.ModelSelect2Multiple(url="autocomplete_linked_resource"),
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["linked_resources"].choices = self.generate_link_choices()
self.fields["linked_resources"].initial = LinkedResource.get_target_ids(self.instance)

# this is used to automatically validate the POSTed back values
self.fields["linked_resources"].queryset = ResourceBase.objects.exclude(pk=self.instance.id)
# these are the LinkedResource already linked to this resource
self.fields["linked_resources"].initial = LinkedResource.get_target_ids(self.instance).all()
# this is used by the autocomplete view to exclude current resource
self.fields["linked_resources"].widget.forward.append(
dal.forward.Const(
self.instance.id,
"exclude",
)
)

class Meta:
model = ResourceBase
fields = ["linked_resources"]

def generate_link_choices(self, resources=None):
if resources is None:
resources = ResourceBase.objects.exclude(pk=self.instance.id).order_by("title")

return [[obj.id, f"{obj.title} ({obj.polymorphic_ctype.model})"] for obj in resources]

def save_linked_resources(self, links_field="linked_resources"):
# create and fetch desired links
target_ids = []
for res_id in self.cleaned_data[links_field]:
linked, _ = LinkedResource.objects.get_or_create(source=self.instance, target_id=res_id, internal=False)
target_ids.append(res_id)
for res in self.cleaned_data[links_field]:
LinkedResource.objects.get_or_create(source=self.instance, target=res, internal=False)
target_ids.append(res.pk)

Check warning on line 383 in geonode/base/forms.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/forms.py#L382-L383

Added lines #L382 - L383 were not covered by tests

# delete remaining links
# DocumentResourceLink.objects.filter(document_id=self.instance.id).exclude(
# pk__in=[i.pk for i in instances]
# ).delete()
(LinkedResource.objects.filter(source_id=self.instance.id).exclude(target_id__in=target_ids).delete())
(
LinkedResource.objects.filter(source_id=self.instance.id, internal=False)
.exclude(target_id__in=target_ids)
.delete()
)


class ResourceBaseDateTimePicker(DateTimePicker):
Expand Down
79 changes: 60 additions & 19 deletions geonode/base/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,31 @@
#
#########################################################################

import json

Check warning on line 20 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L20

Added line #L20 was not covered by tests
import logging
import os
from django.db.utils import IntegrityError, OperationalError
import requests

from PIL import Image
from io import BytesIO

Check warning on line 25 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L24-L25

Added lines #L24 - L25 were not covered by tests
from uuid import uuid4
from unittest.mock import patch, Mock
from guardian.shortcuts import assign_perm

Check warning on line 28 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L28

Added line #L28 was not covered by tests

from django.db.utils import IntegrityError, OperationalError

Check warning on line 30 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L30

Added line #L30 was not covered by tests
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from django.contrib.gis.geos import Polygon, GEOSGeometry
from django.template import Template, Context
from django.contrib.auth import get_user_model
from geonode.storage.manager import storage_manager
from django.test import Client, TestCase, override_settings, SimpleTestCase
from django.shortcuts import reverse
from django.utils import translation
from django.core.files import File
from django.core.management import call_command
from django.core.management.base import CommandError

Check warning on line 42 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L32-L42

Added lines #L32 - L42 were not covered by tests

from PIL import Image
from io import BytesIO
from guardian.shortcuts import assign_perm
from geonode.base.populate_test_data import create_single_dataset

from geonode.maps.models import Map
from geonode.resource.utils import KeywordHandler
from geonode.thumbs import utils as thumb_utils
Expand All @@ -54,15 +65,6 @@
ThesaurusKeyword,
generate_thesaurus_reference,
)
from django.conf import settings
from django.contrib.gis.geos import Polygon, GEOSGeometry
from django.template import Template, Context
from django.contrib.auth import get_user_model
from geonode.storage.manager import storage_manager
from django.test import Client, TestCase, override_settings, SimpleTestCase
from django.shortcuts import reverse
from django.utils import translation

from geonode.base.middleware import ReadOnlyMiddleware, MaintenanceMiddleware
from geonode.base.templatetags.base_tags import get_visibile_resources, facets
from geonode.base.templatetags.thesaurus import (
Expand All @@ -76,10 +78,6 @@
from geonode.base.templatetags.user_messages import show_notification
from geonode import geoserver
from geonode.decorators import on_ogc_backend

from django.core.files import File
from django.core.management import call_command
from django.core.management.base import CommandError
from geonode.base.forms import ThesaurusAvailableForm, THESAURUS_RESULT_LIST_SEPERATOR
from geonode.resource.manager import resource_manager

Expand Down Expand Up @@ -1174,3 +1172,46 @@ def test_regions_are_assigned_if_handler_is_used(self):
self.assertTrue(dataset.regions.exists())
self.assertEqual(1, dataset.regions.count())
self.assertEqual("Global", dataset.regions.first().name)


class LinkedResourcesTest(GeoNodeBaseTestSupport):
def test_autocomplete_linked_resource(self):
d = []
try:
user, _ = get_user_model().objects.get_or_create(username="admin")

Check warning on line 1181 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1177-L1181

Added lines #L1177 - L1181 were not covered by tests

for t in ("dataset1", "dataset2", "other"):
d.append(ResourceBase.objects.create(title=t, owner=user, is_approved=True, is_published=True))

Check warning on line 1184 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1184

Added line #L1184 was not covered by tests

web_client = Client()
web_client.force_login(user)
url_name = "autocomplete_linked_resource"

Check warning on line 1188 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1186-L1188

Added lines #L1186 - L1188 were not covered by tests

# get all resources
response = web_client.get(reverse(url_name))
rjson = response.json()

Check warning on line 1192 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1191-L1192

Added lines #L1191 - L1192 were not covered by tests

self.assertEqual(response.status_code, 200, "Can not get autocomplete API")
self.assertIn("results", rjson, "Can not find results")
self.assertEqual(len(rjson["results"]), 3, "Unexpected results count")

Check warning on line 1196 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1194-L1196

Added lines #L1194 - L1196 were not covered by tests

# filter by title
response = web_client.get(

Check warning on line 1199 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1199

Added line #L1199 was not covered by tests
reverse(url_name),
data={
"q": "dataset",
},
)
rjson = response.json()
self.assertEqual(len(rjson["results"]), 2, "Unexpected results count")

Check warning on line 1206 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1205-L1206

Added lines #L1205 - L1206 were not covered by tests

# filter by title, exclude
response = web_client.get(

Check warning on line 1209 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1209

Added line #L1209 was not covered by tests
reverse(url_name), data={"q": "dataset", "forward": json.dumps({"exclude": d[0].id})}
)
rjson = response.json()
self.assertEqual(len(rjson["results"]), 1, "Unexpected results count")

Check warning on line 1213 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1212-L1213

Added lines #L1212 - L1213 were not covered by tests

finally:
for _ in d:
_.delete()

Check warning on line 1217 in geonode/base/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/tests.py#L1217

Added line #L1217 was not covered by tests
6 changes: 6 additions & 0 deletions geonode/base/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
ResourceBaseAutocomplete,
HierarchicalKeywordAutocomplete,
ThesaurusKeywordLabelAutocomplete,
LinkedResourcesAutocomplete,
)


Expand All @@ -36,6 +37,11 @@
ResourceBaseAutocomplete.as_view(),
name="autocomplete_base",
),
url(
r"^autocomplete_linked_resource/$",
LinkedResourcesAutocomplete.as_view(),
name="autocomplete_linked_resource",
),
url(
r"^autocomplete_region/$",
RegionAutocomplete.as_view(),
Expand Down
22 changes: 22 additions & 0 deletions geonode/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,28 @@ def get_queryset(self):
)[:100]


class LinkedResourcesAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = ResourceBase.objects.order_by("title")

Check warning on line 287 in geonode/base/views.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/views.py#L287

Added line #L287 was not covered by tests

if self.q:
qs = qs.filter(title__icontains=self.q)

Check warning on line 290 in geonode/base/views.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/views.py#L290

Added line #L290 was not covered by tests

if self.forwarded and "exclude" in self.forwarded:
qs = qs.exclude(pk=self.forwarded["exclude"])

Check warning on line 293 in geonode/base/views.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/views.py#L293

Added line #L293 was not covered by tests

return get_visible_resources(

Check warning on line 295 in geonode/base/views.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/views.py#L295

Added line #L295 was not covered by tests
qs,
self.request.user if self.request else None,
admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
unpublished_not_visible=settings.RESOURCE_PUBLISHING,
private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES,
)

def get_result_label(self, result):
return f"{result.title} [{_(result.polymorphic_ctype.model)}]"

Check warning on line 304 in geonode/base/views.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/views.py#L304

Added line #L304 was not covered by tests


class RegionAutocomplete(SimpleSelect2View):
model = Region
filter_arg = "name__icontains"
Expand Down
16 changes: 0 additions & 16 deletions geonode/documents/templates/documents/document_metadata.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,3 @@ <h2>{% trans "Metadata Provider" %}</h2>
</div>
</div>
{% endblock body_outer %}

{% block extra_script %}
{{ block.super }}
<script type="text/javascript">
$("#id_resource-linked_resources").select2({
placeholder: "{% trans "Select an option" %}",
allowClear: true
});
</script>
<style>
#s2id_id_resource-linked_resources {
width: 600px;
height: 100%;
}
</style>
{% endblock extra_script %}
2 changes: 1 addition & 1 deletion geonode/documents/templates/layouts/doc_panels.html
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
{% endblock doc_title %}
{% block doc_linked_resources %}
<div id="req_item">
<span><label for="{{ document_form.linked_resources|id }}">{{ document_form.linked_resources.label }}</label></span>
<span><label for="{{ document_form.linked_resources|id }}">{% trans "Related resources" %}</label></span>
{{ document_form.linked_resources }}
</div>
{% endblock doc_linked_resources %}
Expand Down
12 changes: 6 additions & 6 deletions geonode/documents/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,9 +655,9 @@ def test_create_document_with_links(self):

mixin1 = LinkedResourceForm()
mixin1.instance = d
mixin1.cleaned_data = dict(
linked_resources=[r.id for r in resources],
)
mixin1.cleaned_data = {

Check warning on line 658 in geonode/documents/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/documents/tests.py#L658

Added line #L658 was not covered by tests
"linked_resources": resources,
}
mixin1.save_linked_resources()

for resource in resources:
Expand All @@ -668,9 +668,9 @@ def test_create_document_with_links(self):

mixin2 = LinkedResourceForm()
mixin2.instance = d
mixin2.cleaned_data = dict(
linked_resources=[r.id for r in layers],
)
mixin2.cleaned_data = {

Check warning on line 671 in geonode/documents/tests.py

View check run for this annotation

Codecov / codecov/patch

geonode/documents/tests.py#L671

Added line #L671 was not covered by tests
"linked_resources": layers,
}
mixin2.save_linked_resources()

for resource in layers:
Expand Down
16 changes: 0 additions & 16 deletions geonode/geoapps/templates/apps/app_metadata.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,3 @@ <h2>{% trans "Metadata Provider" %}</h2>

{{ block.super }}
{% endblock body_outer %}

{% block extra_script %}
{{ block.super }}
<script type="text/javascript">
$("#id_resource-linked_resources").select2({
placeholder: "{% trans "Select an option" %}",
allowClear: true
});
</script>
<style>
#s2id_id_resource-linked_resources {
width: 600px;
height: 100%;
}
</style>
{% endblock extra_script %}
2 changes: 1 addition & 1 deletion geonode/geoapps/templates/layouts/app_panels.html
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@
{% endblock %}
{% block geoapp_linked_resources %}
<div id="req_item">
<span><label for="{{ geoapp_form.linked_resources|id }}">{{ geoapp_form.linked_resources.label }}</label></span>
<span><label for="{{ geoapp_form.linked_resources|id }}">{% trans "Related resources" %}</label></span>
{{ geoapp_form.linked_resources }}
</div>
{% endblock geoapp_linked_resources %}
Expand Down
16 changes: 0 additions & 16 deletions geonode/layers/templates/datasets/dataset_metadata.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,3 @@ <h2>{% trans "Metadata Provider" %}</h2>

{{ block.super }}
{% endblock body_outer %}

{% block extra_script %}
{{ block.super }}
<script type="text/javascript">
$("#id_resource-linked_resources").select2({
placeholder: "{% trans "Select an option" %}",
allowClear: true
});
</script>
<style>
#s2id_id_resource-linked_resources {
width: 600px;
height: 100%;
}
</style>
{% endblock extra_script %}
2 changes: 1 addition & 1 deletion geonode/layers/templates/layouts/panels.html
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@
{% endblock dataset_title %}
{% block dataset_linked_resources %}
<div id="req_item">
<span><label for="{{ dataset_form.linked_resources|id }}">{{ dataset_form.linked_resources.label }}</label></span>
<span><label for="{{ dataset_form.linked_resources|id }}">{% trans "Related resources" %}</label></span>
{{ dataset_form.linked_resources }}
</div>
{% endblock dataset_linked_resources %}
Expand Down
Binary file modified geonode/locale/it/LC_MESSAGES/django.mo
Binary file not shown.
10 changes: 9 additions & 1 deletion geonode/locale/it/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ msgstr ""
"Project-Id-Version: GeoNode\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-08 09:02+0300\n"
"PO-Revision-Date: 2023-07-20 11:35+0200\n"
"PO-Revision-Date: 2023-10-11 11:47+0200\n"
"Last-Translator: Julien Collaer <[email protected]>\n"
"Language-Team: Italian (http://www.transifex.com/geonode/geonode/language/it/)\n"
"Language: it\n"
Expand Down Expand Up @@ -947,6 +947,9 @@ msgstr "Classifica velocità"
msgid "Link to"
msgstr "Collegamento a"

msgid "Related resources"
msgstr "Risorse correlate"

msgid "File"
msgstr "File"

Expand Down Expand Up @@ -3283,6 +3286,11 @@ msgid_plural "Maps"
msgstr[0] "Mappa"
msgstr[1] "Mappe"

msgid "map"
msgid_plural "maps"
msgstr[0] "mappa"
msgstr[1] "mappe"

msgid "Map Layers"
msgstr "Livelli della mappa"

Expand Down
2 changes: 1 addition & 1 deletion geonode/maps/templates/layouts/map_panels.html
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@
{% endblock map_title %}
{% block map_linked_resources %}
<div id="req_item">
<span><label for="{{ map_form.linked_resources|id }}">{{ map_form.linked_resources.label }}</label></span>
<span><label for="{{ map_form.linked_resources|id }}">{% trans "Related resources" %}</label></span>
{{ map_form.linked_resources }}
</div>
{% endblock map_linked_resources %}
Expand Down
Loading

0 comments on commit e6a8ef8

Please sign in to comment.