diff --git a/apis_core/generic/abc.py b/apis_core/generic/abc.py
index 72d500aa2..c6c7f5444 100644
--- a/apis_core/generic/abc.py
+++ b/apis_core/generic/abc.py
@@ -25,6 +25,10 @@ def get_edit_url(self):
ct = ContentType.objects.get_for_model(self)
return reverse("apis_core:generic:update", args=[ct, self.id])
+ def get_enrich_url(self):
+ ct = ContentType.objects.get_for_model(self)
+ return reverse("apis_core:generic:enrich", args=[ct, self.id])
+
def get_absolute_url(self):
ct = ContentType.objects.get_for_model(self)
return reverse("apis_core:generic:detail", args=[ct, self.id])
diff --git a/apis_core/generic/forms/__init__.py b/apis_core/generic/forms/__init__.py
index 7b788a9e0..0ff76306b 100644
--- a/apis_core/generic/forms/__init__.py
+++ b/apis_core/generic/forms/__init__.py
@@ -93,3 +93,21 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.add_input(Submit("submit", "Merge"))
+
+
+class GenericEnrichForm(forms.Form):
+ def __init__(self, *args, **kwargs):
+ data = kwargs.pop("data", {})
+ instance = kwargs.pop("instance", None)
+ super().__init__(*args, **kwargs)
+ for key, value in data.items():
+ update_key = f"update_{key}"
+ self.fields[update_key] = forms.BooleanField(
+ required=False,
+ label=f"Update {key} from {getattr(instance, key)} to {value}",
+ )
+
+ self.fields[key] = forms.CharField(initial=value, required=False)
+ self.fields[key].widget = self.fields[key].hidden_widget()
+ self.helper = FormHelper()
+ self.helper.add_input(Submit("submit", "Submit"))
diff --git a/apis_core/generic/templates/generic/generic_enrich.html b/apis_core/generic/templates/generic/generic_enrich.html
new file mode 100644
index 000000000..286e527a7
--- /dev/null
+++ b/apis_core/generic/templates/generic/generic_enrich.html
@@ -0,0 +1,14 @@
+{% extends basetemplate|default:"base.html" %}
+{% load generic %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+
+ {% if form.fields %}
+
+ {% crispy form form.helper %}
+ {% endif %}
+
+{% endblock %}
diff --git a/apis_core/generic/urls.py b/apis_core/generic/urls.py
index c73a27a8f..62cc4f542 100644
--- a/apis_core/generic/urls.py
+++ b/apis_core/generic/urls.py
@@ -54,6 +54,7 @@ def to_url(self, value):
views.MergeWith.as_view(),
name="merge",
),
+ path("enrich/", views.Enrich.as_view(), name="enrich"),
path("autocomplete", views.Autocomplete.as_view(), name="autocomplete"),
path("import", views.Import.as_view(), name="import"),
path(
diff --git a/apis_core/generic/views.py b/apis_core/generic/views.py
index 7faecc6c1..a27f2c961 100644
--- a/apis_core/generic/views.py
+++ b/apis_core/generic/views.py
@@ -6,8 +6,9 @@
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
+from django.core.exceptions import ImproperlyConfigured
from django.forms import modelform_factory
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, redirect
from django.template.exceptions import TemplateDoesNotExist
from django.template.loader import select_template
from django.urls import reverse, reverse_lazy
@@ -20,11 +21,17 @@
from django_tables2.columns import library
from django_tables2.tables import table_factory
+from apis_core.apis_metainfo.models import Uri
from apis_core.core.mixins import ListViewObjectFilterMixin
-from apis_core.utils.helpers import create_object_from_uri
+from apis_core.utils.helpers import create_object_from_uri, get_importer_for_model
from .filtersets import GenericFilterSet
-from .forms import GenericImportForm, GenericMergeForm, GenericModelForm
+from .forms import (
+ GenericEnrichForm,
+ GenericImportForm,
+ GenericMergeForm,
+ GenericModelForm,
+)
from .helpers import (
first_member_match,
generate_search_filter,
@@ -382,5 +389,78 @@ def form_valid(self, form):
messages.info(self.request, f"Merged values of {self.other} into {self.object}")
return super().form_valid(form)
+
+class Enrich(GenericModelMixin, PermissionRequiredMixin, FormView):
+ """
+ Enrich an entity with data from an external source
+ If so, it uses the proper Importer to get the data from the Uri and
+ provides the user with a form to select the fields that should be updated.
+ """
+
+ permission_action_required = "change"
+ template_name = "generic/generic_enrich.html"
+ form_class = GenericEnrichForm
+ importer_class = None
+
+ def setup(self, *args, **kwargs):
+ super().setup(*args, **kwargs)
+ self.object = get_object_or_404(self.model, pk=self.kwargs["pk"])
+ self.uri = self.request.GET.get("uri")
+ if not self.uri:
+ messages.error(self.request, "No uri parameter specified.")
+ self.importer_class = get_importer_for_model(self.model)
+
+ def get(self, *args, **kwargs):
+ if self.uri.isdigit():
+ return redirect(self.object.get_merge_url(self.uri))
+ try:
+ uriobj = Uri.objects.get(uri=self.uri)
+ if uriobj.root_object.id != self.object.id:
+ messages.info(
+ self.request,
+ f"Object with URI {self.uri} already exists, you were redirected to the merge form.",
+ )
+ return redirect(self.object.get_merge_url(uriobj.root_object.id))
+ except Uri.DoesNotExist:
+ pass
+ return super().get(*args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+ ctx["object"] = self.object
+ ctx["uri"] = self.uri
+ return ctx
+
+ def get_form_kwargs(self, *args, **kwargs):
+ kwargs = super().get_form_kwargs(*args, **kwargs)
+ kwargs["instance"] = self.object
+ try:
+ importer = self.importer_class(self.uri, self.model)
+ kwargs["data"] = importer.get_data()
+ except ImproperlyConfigured as e:
+ messages.error(self.request, e)
+ return kwargs
+
+ def form_valid(self, form):
+ """
+ Go through all the form fields and extract the ones that
+ start with `update_` and that are set (those are the checkboxes that
+ select which fields to update).
+ Then use the importers `import_into_instance` method to set those
+ fields values on the model instance.
+ """
+ update_fields = [
+ key.removeprefix("update_")
+ for (key, value) in self.request.POST.items()
+ if key.startswith("update_") and value
+ ]
+ importer = self.importer_class(self.uri, self.model)
+ importer.import_into_instance(self.object, fields=update_fields)
+ messages.info(self.request, f"Updated fields {update_fields}")
+ uri, created = Uri.objects.get_or_create(uri=self.uri, root_object=self.object)
+ if created:
+ messages.info(self.request, f"Added uri {self.uri} to {self.object}")
+ return super().form_valid(form)
+
def get_success_url(self):
return self.object.get_absolute_url()