Skip to content

actor_serializer#

DatasetActorSerializer#

Bases: DatasetMemberSerializer

Serializer for dataset actor.

Same actor can be multiple times in the same dataset.

As a special case, allows roles for same actor to be specified in multiple objects. E.g. {"id": x, "roles": ["creator"]} and {"id": x, "roles": ["publisher"]} in the same request will produce {"id": x, "roles": ["creator", "publisher]}.

Source code in src/apps/core/serializers/dataset_actor_serializers/actor_serializer.py
class DatasetActorSerializer(DatasetMemberSerializer):
    """Serializer for dataset actor.

    Same actor can be multiple times in the same dataset.

    As a special case, allows roles for same actor to be specified in multiple objects.
    E.g. `{"id": x, "roles": ["creator"]}` and `{"id": x, "roles": ["publisher"]}`
    in the same request will produce `{"id": x, "roles": ["creator", "publisher]}`.
    """

    id = UUIDOrTagField(required=False)

    # Organization is normally required but can be left out when it can be determined with id.
    # Having it as required here means it shows as required in swagger.
    organization = DatasetOrganizationSerializer(required=True, allow_null=False)
    person = DatasetPersonSerializer(required=False, allow_null=True)

    partial_update_fields = {"id", "roles", "actors_order"}  # Fields allowed for partial update

    def validate_save(self, validated_data, instance=None):
        validated_data = super().validate_save(validated_data, instance)
        if not validated_data.get("organization") and not self.context.get("migrating"):
            raise serializers.ValidationError({"organization": "This field is required"})
        AnyOf(["person", "organization"])(validated_data)

    def to_internal_value(self, data) -> Dict:
        # Make organization field optional when updating actor so it can be determined
        # with id instead of being explicitly defined in the request.
        fields = self.fields
        fields.get("organization").required = False
        fields.get("organization").allow_null = True
        return super().to_internal_value(data)

    def get_dataset_actors(self, dataset) -> Dict[str, DatasetMemberContext]:
        actors = {}

        def add_actor(actor):
            if actor:
                actors[str(actor.id)] = DatasetMemberContext(
                    object=actor, is_existing=True, existing_data=self.get_existing_data(actor)
                )

        for actor in dataset.actors.all():
            add_actor(actor)

        for provenance in dataset.provenance.all():
            for actor in provenance.is_associated_with.all():
                add_actor(actor)

        return actors

    def get_dataset_members(self) -> Dict[str, DatasetMemberContext]:
        if "dataset_actors" not in self.context:
            dataset: Dataset = self.context.get("dataset")
            if dataset:
                self.context["dataset_actors"] = self.get_dataset_actors(dataset)
            else:
                self.context["dataset_actors"] = {}
        return self.context["dataset_actors"]

    def get_comparison_data(self, value, depth=0):
        if depth == 0:  # allow multiple actor references have different roles
            value = {**value}
            value.pop("roles", None)
            value.pop("actors_order", None)
            if not has_values(value, exclude=self.partial_update_fields):
                return None
        return super().get_comparison_data(value, depth)

    def update_save_data(self, member: DatasetMemberContext, validated_data: dict):
        """Merge roles from all actor objects with same id."""
        roles = None
        if member.save_data and (
            validated_data.get("roles") is not None or member.save_data.get("roles") is not None
        ):
            # Combine role lists
            old_roles = member.save_data.get("roles") or []
            new_roles = validated_data.get("roles") or []
            roles = deduplicate_list([*old_roles, *new_roles])

        actors_order = validated_data.get("actors_order")
        if (
            actors_order is not None
            and member.save_data
            and member.save_data.get("actors_order") is not None
        ):
            # Use first order position if there are multiple
            actors_order = min(actors_order, member.save_data.get("actors_order"))

        super().update_save_data(member, validated_data)

        if roles is not None:
            member.save_data["roles"] = roles

        if actors_order is not None:
            member.save_data["actors_order"] = actors_order

    class Meta:
        model = DatasetActor
        fields = ("id", "roles", "person", "organization")

        # Make CommonListSerializer assign actors_order field when serializing Dataset.actors
        ordering_fields = {"Dataset.actors": "actors_order"}

        list_serializer_class = CommonListSerializer

update_save_data(member, validated_data) #

Merge roles from all actor objects with same id.

Source code in src/apps/core/serializers/dataset_actor_serializers/actor_serializer.py
def update_save_data(self, member: DatasetMemberContext, validated_data: dict):
    """Merge roles from all actor objects with same id."""
    roles = None
    if member.save_data and (
        validated_data.get("roles") is not None or member.save_data.get("roles") is not None
    ):
        # Combine role lists
        old_roles = member.save_data.get("roles") or []
        new_roles = validated_data.get("roles") or []
        roles = deduplicate_list([*old_roles, *new_roles])

    actors_order = validated_data.get("actors_order")
    if (
        actors_order is not None
        and member.save_data
        and member.save_data.get("actors_order") is not None
    ):
        # Use first order position if there are multiple
        actors_order = min(actors_order, member.save_data.get("actors_order"))

    super().update_save_data(member, validated_data)

    if roles is not None:
        member.save_data["roles"] = roles

    if actors_order is not None:
        member.save_data["actors_order"] = actors_order

DatasetActorProvenanceSerializer#

Bases: DatasetActorSerializer

Source code in src/apps/core/serializers/dataset_actor_serializers/actor_serializer.py
class DatasetActorProvenanceSerializer(DatasetActorSerializer):
    class Meta(DatasetActorSerializer.Meta):
        fields = ("id", "person", "organization")  # no roles