<commit_before>"""Metadata Sheet."""
from datetime import datetime
from logging import getLogger

from pyramid.registry import Registry
import colander

from adhocracy_core.interfaces import IResource
from adhocracy_core.interfaces import ISheet
from adhocracy_core.interfaces import SheetToSheet
from adhocracy_core.events import ResourceSheetModified
from adhocracy_core.sheets import add_sheet_to_registry
from adhocracy_core.sheets import sheet_metadata_defaults
from adhocracy_core.sheets.principal import IUserBasic
from adhocracy_core.schema import Boolean
from adhocracy_core.schema import DateTime
from adhocracy_core.schema import Reference
from adhocracy_core.utils import get_sheet
from adhocracy_core.utils import get_user


logger = getLogger(__name__)


class IMetadata(ISheet):

    """Market interface for the metadata sheet."""


class MetadataCreatorsReference(SheetToSheet):

    """Metadata sheet creators reference."""

    source_isheet = IMetadata
    source_isheet_field = 'creator'
    target_isheet = IUserBasic


class MetadataModifiedByReference(SheetToSheet):

    """Points to the last person who modified a resource."""

    source_isheet = IMetadata
    source_isheet_field = 'modified_by'
    target_isheet = IUserBasic


def resource_modified_metadata_subscriber(event):
    """Update the `modified_by` and `modification_date` metadata."""
    sheet = get_sheet(event.object, IMetadata, registry=event.registry)
    request = event.request
    modified_by = None if request is None else get_user(request)
    sheet.set({'modified_by': modified_by,
               'modification_date': datetime.now()},
              send_event=False,
              registry=event.registry,
              request=request,
              force=True)


class MetadataSchema(colander.MappingSchema):

    """Metadata sheet data structure.

    `creation_date`: Creation date of this resource. defaults to now.
    `item_creation_date`: Equals creation date for ISimple/IPool, equals
                          the item creation date for
                          :class:`adhocracy_core.interfaces.IItemVersion`.
                          This exists to ease the frontend end development.
                          This may go away if we have a high level API
                          to make :class:`adhocracy_core.interfaces.Item`
                         /`IItemVersion` one `thing`.
                         defaults to now.
    `creator`: creator (user resources) of this resource.
    `modified_by`: the last person (user resources) who modified a resource,
                   initally the creator
    `modification_date`: Modification date of this resource. defaults to now.
    `deleted`: whether the resource is marked as deleted (only shown to those
               that specifically ask for it)
    `hidden`: whether the resource is marked as hidden (only shown to those
               that have special permissions and ask for it)
    """

    creator = Reference(reftype=MetadataCreatorsReference, readonly=True)
    creation_date = DateTime(missing=colander.drop, readonly=True)
    item_creation_date = DateTime(missing=colander.drop, readonly=True)
    modified_by = Reference(reftype=MetadataModifiedByReference, readonly=True)
    modification_date = DateTime(missing=colander.drop, readonly=True)
    deleted = Boolean()
    hidden = Boolean()


metadata_metadata = sheet_metadata_defaults._replace(
    isheet=IMetadata,
    schema_class=MetadataSchema,
    editable=True,
    creatable=True,
    readable=True,
)


def is_deleted(resource: IResource) -> dict:
    """Check whether a resource is deleted.

    This also returns True for descendants of deleted resources, as a positive
    deleted status is inherited.
    """
    context = resource
    while context is not None:
        if getattr(context, 'deleted', False):
            return True
        context = getattr(context, '__parent__', None)
    return False


def is_hidden(resource: IResource) -> dict:
    """Check whether a resource is hidden.

    This also returns True for descendants of hidden resources, as a positive
    hidden status is inherited.
    """
    context = resource
    while context is not None:
        if getattr(context, 'hidden', False):
            return True
        context = getattr(context, '__parent__', None)
    return False


def view_blocked_by_metadata(resource: IResource,
                             registry: Registry) -> dict:
    """
    Return a dict with an explanation if viewing this resource is not allowed.

    This is the case if the resources provides metadata and the `deleted`
    or `hidden` flag is set.<commit_msg>gardening: use pyramid.lineage to travers resource parents<commit_after>"""Metadata Sheet."""
from datetime import datetime
from logging import getLogger

from pyramid.registry import Registry
from pyramid.location import lineage
import colander

from adhocracy_core.interfaces import IResource
from adhocracy_core.interfaces import ISheet
from adhocracy_core.interfaces import SheetToSheet
from adhocracy_core.events import ResourceSheetModified
from adhocracy_core.sheets import add_sheet_to_registry
from adhocracy_core.sheets import sheet_metadata_defaults
from adhocracy_core.sheets.principal import IUserBasic
from adhocracy_core.schema import Boolean
from adhocracy_core.schema import DateTime
from adhocracy_core.schema import Reference
from adhocracy_core.utils import get_sheet
from adhocracy_core.utils import get_user


logger = getLogger(__name__)


class IMetadata(ISheet):

    """Market interface for the metadata sheet."""


class MetadataCreatorsReference(SheetToSheet):

    """Metadata sheet creators reference."""

    source_isheet = IMetadata
    source_isheet_field = 'creator'
    target_isheet = IUserBasic


class MetadataModifiedByReference(SheetToSheet):

    """Points to the last person who modified a resource."""

    source_isheet = IMetadata
    source_isheet_field = 'modified_by'
    target_isheet = IUserBasic


def resource_modified_metadata_subscriber(event):
    """Update the `modified_by` and `modification_date` metadata."""
    sheet = get_sheet(event.object, IMetadata, registry=event.registry)
    request = event.request
    modified_by = None if request is None else get_user(request)
    sheet.set({'modified_by': modified_by,
               'modification_date': datetime.now()},
              send_event=False,
              registry=event.registry,
              request=request,
              force=True)


class MetadataSchema(colander.MappingSchema):

    """Metadata sheet data structure.

    `creation_date`: Creation date of this resource. defaults to now.
    `item_creation_date`: Equals creation date for ISimple/IPool, equals
                          the item creation date for
                          :class:`adhocracy_core.interfaces.IItemVersion`.
                          This exists to ease the frontend end development.
                          This may go away if we have a high level API
                          to make :class:`adhocracy_core.interfaces.Item`
                         /`IItemVersion` one `thing`.
                         defaults to now.
    `creator`: creator (user resources) of this resource.
    `modified_by`: the last person (user resources) who modified a resource,
                   initally the creator
    `modification_date`: Modification date of this resource. defaults to now.
    `deleted`: whether the resource is marked as deleted (only shown to those
               that specifically ask for it)
    `hidden`: whether the resource is marked as hidden (only shown to those
               that have special permissions and ask for it)
    """

    creator = Reference(reftype=MetadataCreatorsReference, readonly=True)
    creation_date = DateTime(missing=colander.drop, readonly=True)
    item_creation_date = DateTime(missing=colander.drop, readonly=True)
    modified_by = Reference(reftype=MetadataModifiedByReference, readonly=True)
    modification_date = DateTime(missing=colander.drop, readonly=True)
    deleted = Boolean()
    hidden = Boolean()


metadata_metadata = sheet_metadata_defaults._replace(
    isheet=IMetadata,
    schema_class=MetadataSchema,
    editable=True,
    creatable=True,
    readable=True,
)


def is_deleted(resource: IResource) -> dict:
    """Check whether a resource is deleted.

    This also returns True for descendants of deleted resources, as a positive
    deleted status is inherited.
    """
    for context in lineage(resource):
        if getattr(context, 'deleted', False):
            return True
    return False


def is_hidden(resource: IResource) -> dict:
    """Check whether a resource is hidden.

    This also returns True for descendants of hidden resources, as a positive
    hidden status is inherited.
    """
    for context in lineage(resource):
        if getattr(context, 'hidden', False):
            return True
    return False


def view_blocked_by_metadata(resource: IResource,
                             registry: Registry) -> dict:
    """
    Return a dict with an explanation if viewing this resource is not allowed.

    This is the case if the resources provides metadata and the `deleted`
    or `hidden` flag is set.
{'fraction_non_alphanumeric': 0.06779478335848244, 'fraction_numerical': 0.0, 'mean_word_length': 3.192950745594216, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2975765', 'n_tokens_mistral': 2460, 'n_tokens_neox': 2345, 'n_words': 809}