From 3a54a03b179eb858e6376a8641497aac86a7e62a Mon Sep 17 00:00:00 2001 From: alejandromumo Date: Thu, 7 Mar 2024 15:45:25 +0100 Subject: [PATCH] records: refactored 'status' as a systemfield * 'status' is not a db column anymore, instead it is a systemfield --- .../communities/records/api.py | 3 +- .../communities/records/models.py | 30 +----- .../records/systemfields/community_status.py | 100 ++++++++++++++++++ .../records/systemfields/is_safelisted.py | 2 +- tests/communities/test_safelist.py | 8 +- tests/communities/test_services.py | 5 +- 6 files changed, 112 insertions(+), 36 deletions(-) create mode 100644 invenio_communities/communities/records/systemfields/community_status.py diff --git a/invenio_communities/communities/records/api.py b/invenio_communities/communities/records/api.py index 05901fd18..647aa5ed3 100644 --- a/invenio_communities/communities/records/api.py +++ b/invenio_communities/communities/records/api.py @@ -34,6 +34,7 @@ from ..dumpers.featured import FeaturedDumperExt from . import models from .systemfields.access import CommunityAccessField +from .systemfields.community_status import CommunityStatusField from .systemfields.deletion_status import CommunityDeletionStatusField from .systemfields.parent_community import ParentCommunityField from .systemfields.pidslug import PIDSlugField @@ -121,7 +122,7 @@ class Community(Record): custom=CustomFieldsRelation("COMMUNITIES_CUSTOM_FIELDS"), ) - status = ModelField("status", dump_type=str) + status = CommunityStatusField("status") is_safelisted = IsSafelistedField("is_safelisted") diff --git a/invenio_communities/communities/records/models.py b/invenio_communities/communities/records/models.py index 00fb03b75..f4ad14d99 100644 --- a/invenio_communities/communities/records/models.py +++ b/invenio_communities/communities/records/models.py @@ -8,7 +8,7 @@ # under the terms of the MIT License; see LICENSE file for more details. """Community database models.""" -import enum + from datetime import datetime from invenio_db import db @@ -22,28 +22,6 @@ from .systemfields.deletion_status import CommunityDeletionStatusEnum -class CommunityStatusEnum(enum.Enum): - """Community status enum.""" - - NEW = "N" - VERIFIED = "V" - MODERATED = "M" - - def __str__(self): - """Return its value.""" - return self.value - - def __eq__(self, __value) -> bool: - """Check if the value is equal to the enum value. - - Supports comparison with string values. - """ - if isinstance(__value, str): - return self.value == __value - - return super().__eq__(__value) - - class CommunityMetadata(db.Model, RecordMetadataBase): """Represent a community.""" @@ -61,12 +39,6 @@ class CommunityMetadata(db.Model, RecordMetadataBase): default=CommunityDeletionStatusEnum.PUBLISHED.value, ) - status = db.Column( - ChoiceType(CommunityStatusEnum, impl=db.String(1)), - nullable=False, - default=CommunityStatusEnum.NEW.value, - ) - class CommunityFileMetadata(db.Model, RecordMetadataBase, FileRecordModelMixin): """File associated with a community.""" diff --git a/invenio_communities/communities/records/systemfields/community_status.py b/invenio_communities/communities/records/systemfields/community_status.py new file mode 100644 index 000000000..af1046646 --- /dev/null +++ b/invenio_communities/communities/records/systemfields/community_status.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2024 CERN. +# +# Invenio-communities is free software; you can redistribute it and/or modify +# it under the terms of the MIT License; see LICENSE file for more details. +"""Community status system field.""" + +import enum + +from invenio_records.systemfields import SystemField + + +class CommunityStatusEnum(enum.Enum): + """Community status enum.""" + + NEW = "new" + VERIFIED = "verified" + MODERATED = "moderated" + + +class CommunityStatus: + """The community status of the community.""" + + def __init__(self, status): + """Initialize the community status.""" + self.status = status + + @property + def status(self): + """Get the community status.""" + return self._status.value + + @status.setter + def status(self, value): + """Set the community status.""" + if value is None: + self._status = CommunityStatusEnum.NEW + + elif isinstance(value, str): + self._status = CommunityStatusEnum(value) + + elif isinstance(value, CommunityStatusEnum): + self._status = value + + else: + raise ValueError(f"Invalid value for community community status: {value}") + + def __repr__(self): + """Return repr(self).""" + return f"" + + def __str__(self): + """Return str(self).""" + return self.status + + def __eq__(self, other): + """Check if self and other are equal. + + This allows checking against other instances of the same type, strings, + and ``CommunityStatusEnum`` values. + """ + if isinstance(other, type(self)): + return self.status == other.status + + elif isinstance(other, CommunityStatusEnum): + return self.status == other.value + + elif isinstance(other, str): + return self.status == other + + return False + + +class CommunityStatusField(SystemField): + """System field for the community status.""" + + # + # Data descriptor methods (i.e. attribute access) + # + def __get__(self, record, owner=None): + """Get the status of the community.""" + if record is None: + return self # returns the field itself. + + status = self._get_cache(record) or CommunityStatus(record.get("status")) + + self._set_cache(record, status) + return status + + def __set__(self, record, value): + """Set the status of the community.""" + status = CommunityStatus(value) + self._set_cache(record, status) + assert True + + def pre_commit(self, record): + """Dump the deletion status to the community before committing.""" + status = self._get_cache(record) or CommunityStatus(None) + record[self.key] = status.status diff --git a/invenio_communities/communities/records/systemfields/is_safelisted.py b/invenio_communities/communities/records/systemfields/is_safelisted.py index dca121748..310e9d592 100644 --- a/invenio_communities/communities/records/systemfields/is_safelisted.py +++ b/invenio_communities/communities/records/systemfields/is_safelisted.py @@ -8,7 +8,7 @@ from invenio_records_resources.records.systemfields.calculated import CalculatedField -from ..models import CommunityStatusEnum +from ..systemfields.community_status import CommunityStatusEnum class IsSafelistedField(CalculatedField): diff --git a/tests/communities/test_safelist.py b/tests/communities/test_safelist.py index c24e1d6ea..5512b683c 100644 --- a/tests/communities/test_safelist.py +++ b/tests/communities/test_safelist.py @@ -10,15 +10,15 @@ from invenio_db import db -from invenio_communities.communities.records.models import CommunityStatusEnum +from invenio_communities.communities.records.systemfields.community_status import ( + CommunityStatusEnum, +) def test_safelist_computed_by_verified_status( community_service, minimal_community, location, es_clear, unverified_user ): - """ - Test that the safelist feature for communities is computed correctly based on the verified status. - """ + """Test that the safelist feature for communities is computed correctly based on the verified status.""" # Create a comunity # Flag it as "verified" # Validate that the computed field "is_verified" is set to "True" diff --git a/tests/communities/test_services.py b/tests/communities/test_services.py index 1597687c1..60a55681a 100644 --- a/tests/communities/test_services.py +++ b/tests/communities/test_services.py @@ -21,7 +21,9 @@ from invenio_records_resources.services.errors import PermissionDeniedError from marshmallow import ValidationError -from invenio_communities.communities.records.models import CommunityStatusEnum +from invenio_communities.communities.records.systemfields.community_status import ( + CommunityStatusEnum, +) from invenio_communities.communities.records.systemfields.deletion_status import ( CommunityDeletionStatusEnum, ) @@ -766,6 +768,7 @@ def test_bulk_update_parent_overwrite( def test_status_new(community_service, minimal_community, location, es_clear, owner): + """Test the status of a new community.""" c_data = deepcopy(minimal_community) c_data["slug"] = "test_status_new" co = community_service.create(data=c_data, identity=owner.identity)