Skip to content

Commit b303121

Browse files
mwallschlaegerMalteIwanickiahmdthrgannebammgiohappy
authored
[Fixes #10290] complete ISO contact roles per ressource base with multiplicity (#10367)
* issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * fixed typo * created a centralized enum with the roles. added contacts to geonode_metadata_full.html. refactored * multiple poc are displayed in _resourcebase_info_panel * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Updates for PATCH for multiple contacts along with tests for each role. * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * Fixed default contact roles for new resource and added tests * black * black * Fixed AttributeError with TaggitProfileSelect2Custom * Fixes #10290 complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * - Fix formatting * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * [Fixes #10290] complete ISO contact roles per ressource base with multiplicity * [Fixes #10290] complete ISO contact roles per ressource base with multiplicity * Revert "[Fixes #10290] complete ISO contact roles per ressource base with multiplicity" This reverts commit 44b294b. * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity * revert unintended changes from recent commit * revert unintended changes from recent commit * revert unintended changes from recent commit * revert unintended changes from recent commit --------- Co-authored-by: Malte Iwanicki <[email protected]> Co-authored-by: Malte Iwanicki <[email protected]> Co-authored-by: ahmdthr <[email protected]> Co-authored-by: Florian Hoedt <[email protected]> Co-authored-by: Giovanni Allegri <[email protected]> Co-authored-by: Alessio Fabiani <[email protected]> Co-authored-by: Marcel Wallschlaeger <[email protected]>
1 parent f1a905b commit b303121

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2106
-577
lines changed

geonode/base/api/serializers.py

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
from django.forms.models import model_to_dict
2929
from django.contrib.auth import get_user_model
3030
from django.db.models.query import QuerySet
31+
from geonode.people import Roles
3132
from django.http import QueryDict
3233

3334
from deprecated import deprecated
3435
from rest_framework import serializers
3536
from rest_framework_gis import fields
3637
from rest_framework.reverse import reverse, NoReverseMatch
38+
from rest_framework.exceptions import ParseError
3739

3840
from dynamic_rest.serializers import DynamicEphemeralSerializer, DynamicModelSerializer
3941
from dynamic_rest.fields.fields import DynamicRelationField, DynamicComputedField
@@ -379,15 +381,44 @@ def to_representation(self, instance):
379381

380382

381383
class ContactRoleField(DynamicComputedField):
382-
def __init__(self, contat_type, **kwargs):
383-
self.contat_type = contat_type
384+
default_error_messages = {
385+
"required": ("ContactRoleField This field is required."),
386+
}
387+
388+
def __init__(self, contact_type, **kwargs):
389+
self.contact_type = contact_type
384390
super().__init__(**kwargs)
385391

386392
def get_attribute(self, instance):
387-
return getattr(instance, self.contat_type)
393+
return getattr(instance, self.contact_type)
388394

389395
def to_representation(self, value):
390-
return UserSerializer(embed=True, many=False).to_representation(value)
396+
return [UserSerializer(embed=True, many=False).to_representation(v) for v in value]
397+
398+
def get_pks_of_users_to_set(self, value):
399+
pks_of_users_to_set = []
400+
for val in value:
401+
# make it possible to set contact roles via username or pk through API
402+
if "username" in val and "pk" in val:
403+
pk = val["pk"]
404+
username = val["username"]
405+
pk_user = get_user_model().objects.get(pk=pk)
406+
username_user = get_user_model().objects.get(username=username)
407+
if pk_user.pk != username_user.pk:
408+
raise ParseError(
409+
detail=f"user with pk: {pk} and username: {username} is not the same ... ", code=403
410+
)
411+
pks_of_users_to_set.append(pk)
412+
elif "username" in val:
413+
username = val["username"]
414+
username_user = get_user_model().objects.get(username=[username])
415+
pks_of_users_to_set.append(username_user.pk)
416+
elif "pk" in val:
417+
pks_of_users_to_set.append(val["pk"])
418+
return pks_of_users_to_set
419+
420+
def to_internal_value(self, value):
421+
return get_user_model().objects.filter(pk__in=self.get_pks_of_users_to_set(value))
391422

392423

393424
class ExtentBboxField(DynamicComputedField):
@@ -479,16 +510,22 @@ def __init__(self, *args, **kwargs):
479510
self.fields["uuid"] = serializers.CharField(read_only=True)
480511
self.fields["resource_type"] = serializers.CharField(required=False)
481512
self.fields["polymorphic_ctype_id"] = serializers.CharField(read_only=True)
482-
self.fields["owner"] = DynamicRelationField(
483-
UserSerializer, embed=True, many=False, read_only=True, required=False
484-
)
485-
self.fields["poc"] = ContactRoleField("poc", read_only=True)
486-
self.fields["metadata_author"] = ContactRoleField("metadata_author", read_only=True)
487-
self.fields["title"] = serializers.CharField()
513+
self.fields["owner"] = DynamicRelationField(UserSerializer, embed=True, many=False, read_only=True)
514+
self.fields["metadata_author"] = ContactRoleField(Roles.METADATA_AUTHOR.name, required=False)
515+
self.fields["processor"] = ContactRoleField(Roles.PROCESSOR.name, required=False)
516+
self.fields["publisher"] = ContactRoleField(Roles.PUBLISHER.name, required=False)
517+
self.fields["custodian"] = ContactRoleField(Roles.CUSTODIAN.name, required=False)
518+
self.fields["poc"] = ContactRoleField(Roles.POC.name, required=False)
519+
self.fields["distributor"] = ContactRoleField(Roles.DISTRIBUTOR.name, required=False)
520+
self.fields["resource_user"] = ContactRoleField(Roles.RESOURCE_USER.name, required=False)
521+
self.fields["resource_provider"] = ContactRoleField(Roles.RESOURCE_PROVIDER.name, required=False)
522+
self.fields["originator"] = ContactRoleField(Roles.ORIGINATOR.name, required=False)
523+
self.fields["principal_investigator"] = ContactRoleField(Roles.PRINCIPAL_INVESTIGATOR.name, required=False)
524+
self.fields["title"] = serializers.CharField(required=False)
488525
self.fields["abstract"] = serializers.CharField(required=False)
489526
self.fields["attribution"] = serializers.CharField(required=False)
490527
self.fields["doi"] = serializers.CharField(required=False)
491-
self.fields["alternate"] = serializers.CharField(read_only=True)
528+
self.fields["alternate"] = serializers.CharField(read_only=True, required=False)
492529
self.fields["date"] = serializers.DateTimeField(required=False)
493530
self.fields["date_type"] = serializers.CharField(required=False)
494531
self.fields["temporal_extent_start"] = serializers.DateTimeField(required=False)
@@ -559,6 +596,14 @@ class Meta:
559596
"owner",
560597
"poc",
561598
"metadata_author",
599+
"processor",
600+
"publisher",
601+
"custodian",
602+
"distributor",
603+
"resource_user",
604+
"resource_provider",
605+
"originator",
606+
"principal_investigator",
562607
"keywords",
563608
"tkeywords",
564609
"regions",

geonode/base/forms.py

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@
5757
ThesaurusLabel,
5858
TopicCategory,
5959
)
60-
from geonode.base.widgets import TaggitSelect2Custom
60+
from geonode.base.widgets import TaggitSelect2Custom, TaggitProfileSelect2Custom
6161
from geonode.base.fields import MultiThesauriField
6262
from geonode.documents.models import Document
6363
from geonode.layers.models import Dataset
6464
from geonode.base.utils import validate_extra_metadata, remove_country_from_languagecode
65+
from geonode.people import Roles
6566

6667
logger = logging.getLogger(__name__)
6768

@@ -348,6 +349,16 @@ def _get_thesauro_title_label(item, lang):
348349
return tname.first()
349350

350351

352+
class ContactRoleMultipleChoiceField(forms.ModelMultipleChoiceField):
353+
def clean(self, value) -> QuerySet:
354+
try:
355+
users = get_user_model().objects.filter(username__in=value)
356+
except TypeError:
357+
# value of not supported type ...
358+
raise forms.ValidationError(_("Something went wrong in finding the profile(s) in a contact role form ..."))
359+
return users
360+
361+
351362
class LinkedResourceForm(forms.ModelForm):
352363
linked_resources = forms.ModelMultipleChoiceField(
353364
label=_("Related resources"),
@@ -418,8 +429,8 @@ class ResourceBaseForm(TranslationModelForm, LinkedResourceForm):
418429
data_quality_statement = forms.CharField(label=_("Data quality statement"), required=False, widget=TinyMCE())
419430

420431
owner = forms.ModelChoiceField(
421-
empty_label=_("Owner"),
422-
label=_("Owner"),
432+
empty_label=_(Roles.OWNER.label),
433+
label=_(Roles.OWNER.label),
423434
required=True,
424435
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
425436
widget=autocomplete.ModelSelect2(url="autocomplete_profile"),
@@ -448,20 +459,74 @@ class ResourceBaseForm(TranslationModelForm, LinkedResourceForm):
448459
widget=ResourceBaseDateTimePicker(options={"format": "YYYY-MM-DD HH:mm a"}),
449460
)
450461

451-
poc = forms.ModelChoiceField(
452-
empty_label=_("Person outside GeoNode (fill form)"),
453-
label=_("Point of Contact"),
454-
required=False,
462+
metadata_author = ContactRoleMultipleChoiceField(
463+
label=_(Roles.METADATA_AUTHOR.label),
464+
required=Roles.METADATA_AUTHOR.is_required,
455465
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
456-
widget=autocomplete.ModelSelect2(url="autocomplete_profile"),
466+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
457467
)
458468

459-
metadata_author = forms.ModelChoiceField(
460-
empty_label=_("Person outside GeoNode (fill form)"),
461-
label=_("Metadata Author"),
462-
required=False,
469+
processor = ContactRoleMultipleChoiceField(
470+
label=_(Roles.PROCESSOR.label),
471+
required=Roles.PROCESSOR.is_required,
463472
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
464-
widget=autocomplete.ModelSelect2(url="autocomplete_profile"),
473+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
474+
)
475+
476+
publisher = ContactRoleMultipleChoiceField(
477+
label=_(Roles.PUBLISHER.label),
478+
required=Roles.PUBLISHER.is_required,
479+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
480+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
481+
)
482+
483+
custodian = ContactRoleMultipleChoiceField(
484+
label=_(Roles.CUSTODIAN.label),
485+
required=Roles.CUSTODIAN.is_required,
486+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
487+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
488+
)
489+
490+
poc = ContactRoleMultipleChoiceField(
491+
label=_(Roles.POC.label),
492+
required=Roles.POC.is_required,
493+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
494+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
495+
)
496+
497+
distributor = ContactRoleMultipleChoiceField(
498+
label=_(Roles.DISTRIBUTOR.label),
499+
required=Roles.DISTRIBUTOR.is_required,
500+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
501+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
502+
)
503+
504+
resource_user = ContactRoleMultipleChoiceField(
505+
label=_(Roles.RESOURCE_USER.label),
506+
required=Roles.RESOURCE_USER.is_required,
507+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
508+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
509+
)
510+
511+
resource_provider = ContactRoleMultipleChoiceField(
512+
label=_(Roles.RESOURCE_PROVIDER.label),
513+
required=Roles.RESOURCE_PROVIDER.is_required,
514+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
515+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
516+
)
517+
518+
originator = ContactRoleMultipleChoiceField(
519+
label=_(Roles.ORIGINATOR.label),
520+
required=Roles.ORIGINATOR.is_required,
521+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
522+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
523+
)
524+
525+
principal_investigator = ContactRoleMultipleChoiceField(
526+
label=_(Roles.PRINCIPAL_INVESTIGATOR.label),
527+
required=Roles.PRINCIPAL_INVESTIGATOR.is_required,
528+
queryset=get_user_model().objects.exclude(username="AnonymousUser"),
529+
widget=TaggitProfileSelect2Custom(url="autocomplete_profile"),
465530
)
466531

467532
keywords = TagField(
@@ -512,7 +577,7 @@ def __init__(self, *args, **kwargs):
512577
}
513578
)
514579

515-
if field in ["poc", "owner"] and not self.can_change_perms:
580+
if field in ["owner"] and not self.can_change_perms:
516581
self.fields[field].disabled = True
517582

518583
def disable_keywords_widget_for_non_superuser(self, user):

geonode/base/migrations/0086_linkedresource.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,31 @@
55

66

77
class Migration(migrations.Migration):
8-
98
dependencies = [
10-
('base', '0085_alter_resourcebase_uuid'),
9+
("base", "0085_alter_resourcebase_uuid"),
1110
]
1211

1312
operations = [
1413
migrations.CreateModel(
15-
name='LinkedResource',
14+
name="LinkedResource",
1615
fields=[
17-
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18-
('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='linked_to', to='base.resourcebase')),
19-
('target', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='linked_by', to='base.resourcebase')),
20-
('internal', models.BooleanField(default=False)),
16+
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
17+
(
18+
"source",
19+
models.ForeignKey(
20+
on_delete=django.db.models.deletion.CASCADE, related_name="linked_to", to="base.resourcebase"
21+
),
22+
),
23+
(
24+
"target",
25+
models.ForeignKey(
26+
blank=True,
27+
on_delete=django.db.models.deletion.CASCADE,
28+
related_name="linked_by",
29+
to="base.resourcebase",
30+
),
31+
),
32+
("internal", models.BooleanField(default=False)),
2133
],
2234
),
2335
]

0 commit comments

Comments
 (0)